Repository navigation
Commit 7db39f1
fix(serving): stop a usage-accounting write from 500-ing a served request (#172)
Every authenticated request appends an api_usage row from a worker thread,
and the analytics/admin routers build a throwaway store per request. Each
of those opened its own duckdb.connect() on the usage file. The last close
destroys the DuckDB instance, so a close racing an open leaves the file
attached by two instances and DuckDB raises
BinderException: Unique file handle conflict
That escaped AuthMiddleware and turned requests that had already succeeded
into 500s: the 2026-07-09 Load Test on main saw 19 of 1712 fail across all
six endpoints, every traceback ending in record_api_usage -> connect_duckdb.
Root cause: EmbeddedControlPlaneStore now holds one owning connection per
usage-db path for the life of the process and hands out .cursor() children,
the shape DuckDBPool already uses for the serving database. Call sites are
untouched — they still close() what they are given, and closing a cursor
leaves the connection alive. 80 concurrent usage writes: 80 connects -> 1.
Defence in depth: record_api_usage still raises on exhausted retries (its
caller depends on that to skip the audit publish), but the middleware now
catches it, counts agentflow_usage_record_failures_total, logs
api_usage_record_skipped, and serves the request. Accounting is a
side-channel and must not fail the request it was counting.
Four tests pinned the old per-call-connect mechanism rather than the
behaviour; they now drop the cached connection before injecting their
fault and assert on retry attempts instead of connect counts.
Verified: 1670 unit + 263 integration pass locally; the middleware
regression test fails on the pre-fix tree with the exact BinderException.
The race's timing reproduces only on the CI runner, so the new tests pin
the mechanism that removes it rather than the timing.
Co-authored-by: JuliaEdom <uedomskikh@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>1 parent 617bbb7 commit 7db39f1
10 files changed
Lines changed: 2234 additions & 1855 deletions
File tree
- src/serving
- api
- auth
- control_plane
- tests
- integration
- unit
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
7 | 34 | | |
8 | 35 | | |
9 | 36 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
15 | | - | |
| 15 | + | |
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| |||
110 | 110 | | |
111 | 111 | | |
112 | 112 | | |
113 | | - | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
114 | 133 | | |
115 | 134 | | |
116 | 135 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
0 commit comments