Dike logoDocs

Deploying Dike in mission-critical LegalTech environments requires engineering for resilience against rate limits, transient network partitions, and heavy inference loads.

Rendering diagram...

1. Exponential Backoff with Full Jitter

When handling 429 rate_limited or transient 503 upstream_unavailable errors, implement exponential backoff with full jitter to avoid the "thundering herd" problem:

sleep_time = random(0, min(max_sleep, base_sleep * (2 ^ attempt)))

Python Implementation (tenacity)

import httpx
from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_random_exponential

class DikeTransientError(Exception):
    pass

@retry(
    wait=wait_random_exponential(multiplier=1, max=30),
    stop=stop_after_attempt(5),
    retry=retry_if_exception_type(DikeTransientError),
)
def call_dike_resilient(url: str, payload: dict, api_key: str):
    headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
    with httpx.Client() as client:
        res = client.post(url, headers=headers, json=payload, timeout=20.0)
        
        if res.status_code in (429, 503):
            raise DikeTransientError(f"Transient error [{res.status_code}]: {res.text}")
        
        res.raise_for_status()
        return res.json()

2. In-Memory / Redis Caching for Immutable Statutes

Document text returned by GET /v1/document/{canonical_id} changes very infrequently once promulgated.

  • canonical_id Text (/v1/document/*): Cache in Redis with a TTL of 7 to 30 days.
  • Jurisdictions Catalog (/v1/jurisdictions): Cache in memory for 24 hours.
  • Citation Normalization (/v1/cite): Cache normalized queries for 7 days.
  • Search & Reason (/v1/search, /v1/reason): Cache by exact query hash for 1 hour (or bypass cache if real-time queries are required).

3. Dynamic Rate Limit Budgeting

Inspect the X-RateLimit-Remaining header in background workers (e.g. Celery / BullMQ):

// workers/legal-ingestion-worker.ts
async function processBatch(citations: string[]) {
  for (const citation of citations) {
    const res = await fetch('https://api.dike.it.com/v1/cite', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.DIKE_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ citation }),
    });

    const remaining = parseInt(res.headers.get('X-RateLimit-Remaining') || '10', 10);
    const resetTime = parseInt(res.headers.get('X-RateLimit-Reset') || '0', 10);

    // Proactively throttle worker if remaining quota is low
    if (remaining < 5) {
      const sleepSeconds = Math.max(1, resetTime - Math.floor(Date.now() / 1000));
      console.log(`Approaching quota limit. Throttling worker for ${sleepSeconds}s...`);
      await new Promise((resolve) => setTimeout(resolve, sleepSeconds * 1000));
    }
  }
}

4. Circuit Breakers for Downstream Reasoning

If POST /v1/reason experiences upstream inference delays during peak hours, use a circuit breaker to fail fast or gracefully degrade to POST /v1/search + cached responses:

// Fallback strategy: If reason fails, fall back to pure hybrid search
async function askLegalSafe(question: string) {
  try {
    return await askReason(question);
  } catch (err) {
    console.warn('Reasoning service unavailable. Falling back to hybrid search results...');
    const searchResults = await runSearch(question);
    return {
      answer: 'تعذر الاتصال بمحرك الاستدلال. فيما يلي أهم النصوص القانونية ذات الصلة:',
      citations: searchResults.results.map((r: any, idx: number) => ({
        marker: idx + 1,
        canonical_id: r.canonical_id,
        citation_string: r.citation_string,
        snippet: r.snippet,
        verified: true,
      })),
      isFallback: true,
    };
  }
}

Next Steps

On this page