Sionic AI · 2024.09 — now

Improving Settlement Delay Under Increasing Load

  • Minimized the negative-balance window via Rate Limit + batch interval and API Key cache TTL tuning
  • Secured stability with minimal development, then moved to architecture redesign

Issue

The existing cost-control logic was intentionally designed to be lightweight.

  • The Data Plane calls AI providers based on locally cached API Keys and forwards the results
  • The Control Plane processes billing from call results and suspends the API Key when the balance goes negative
  • During this time, the cached API Key in the Data Plane is not updated, so a gap exists for the duration of the TTL, causing a potential billing leak

Meanwhile, internal traffic spiked and traffic grew with self-hosted model serving, drawing traffic beyond throughput capacity — exposing the limits of the cost-control design.

---
config:
  theme: base
  darkMode: false
  themeVariables:
    background: "#ffffff"
    primaryColor: "#ffffff"
    primaryTextColor: "#111827"
    primaryBorderColor: "#475569"
    lineColor: "#334155"
    edgeLabelBackground: "#ffffff"
---
flowchart LR
  Client["Client"] -->|"LLM call"| B["API Key auth<br/>ACTIVE cache"]
  B --> C["AI provider call"]
  C -->|"call record"| G["Billing"]

  subgraph DP["Data Plane"]
    B
    C
  end

  subgraph CP["Control Plane"]
    direction TB
    G["Billing"]
    G --> H["balance ≤ 0<br/>key SUSPENDED"]
  end

Analysis

Approach consideredEffectCost
Pre-request budget reservationPrevents negative balances at the sourceCP DB lookup per request → severe throughput drop
Immediate evict on key SUSPENDEDRemoves cache gapCP→multi-DP fan-out, added complexity
✅ Rate Limit 1Per-request billing capSimple to implement, coarse control
Rate Limit 2Token-usage-based billing capHigh implementation complexity, low priority for internal/solo-founder customers
✅ Allow negative balanceMaintains throughput, structurally simpleBilling leak occurs, but a commonly adopted trade-off in high-throughput gateways, treated as an acceptable loss
✅ Batch throughput expansionShrinks batch intervalIncreased batch load
✅ Shorter cache TTLShrinks cache gapHigher API Key lookup volume, but proportional to distinct active keys per minute — verified proportional to distinct active keys — confirmed manageable

Initially, the service had a limited audience, so the cost-control level was low — but as platform stability was proven and needs like self-hosted model serving emerged, the target was raised significantly to 1.5M RPM.
Therefore, we addressed this issue with minimal development and monitoring improvements, and quickly moved into the architecture redesign.

Improvement

With minimal additional development and configuration changes, we verified through measurement that the system handles up to 15,000 RPM without issues, securing stability and moving quickly to the next phase.

Phase 1: batch throughput expansion

MetricBeforeAfter
Chunk size1,0005,000
Batch count13
Records per run1,00015,000

However, post-deployment testing revealed that per-chunk processing time was too slow. (5,000 records, 11s)

Phase 2: JFR-guided billing batch query improvement

Profiling revealed status-update-execute averaging 2,845ms per 5,000 records as the core bottleneck, caused by 5,000 composite keys combined into a massive OR condition in a single UPDATE.

JFR — Before: status-update-execute 2,845ms

JFR — After: status-update-execute 99ms

MetricBeforeAfter
Query patternMultiple row-level UPDATEsUPDATE ... FROM (VALUES ...) single query
status-update-execute2,845ms99ms
5,000-record processing time11s1.65s

Billing execution time — Before: 5,000 records taking 11s

Billing execution time — After: 5,000 records taking 1.65s

Phase 3: pagination chunk parallel processing — unnecessary at current level, intentionally not pursued

Switching the chunks processed in Phase 2 to parallel would further reduce processing time.
But 5,000-record processing at 1.65s with batch 1min interval + 3 parallel batches was already sufficient to handle 15,000 RPM, so we held off to move quickly to the next architecture.