Skip to content

fix(sdk): skip discarded catalog read on background tool-sync failure - #23

Open
polylane[bot] wants to merge 1 commit into
mainfrom
polylane/autofix/2jey7mi9mk8y
Open

polylane[bot] wants to merge 1 commit into
mainfrom
polylane/autofix/2jey7mi9mk8y

Conversation

@polylane

@polylane polylane Bot commented Sep 17, 2026

Copy link
Copy Markdown

Fixes: D1 multi-MB read burst from discarded background tool-sync catalog reads

When an executor tool sync cannot list an MCP connection's tools, it re-reads that connection's whole persisted catalog from the database, and it does so even on background refreshes whose returned catalog is thrown away. On a catalog with large tool schemas those reads reach several megabytes: D1 response bytes on the Airbooks account hit 3.8 MB against a ~70 KB baseline in one bucket, with the matching rows-read metric at 19,517 rows, and the same shape recurred every few hours through the week. After this merge the background refresh stamps the sync time and leaves the catalog in place without copying it, while an explicit tool listing still returns the preserved catalog.

flowchart TD
  A["tools read: connection catalog stale"] --> B["background stale-catalog rebuild"]
  B --> C["resolveTools: MCP server HTTP 401"]
  C --> D["preserved-catalog branch reads every tool row incl. schemas"]
  D --> E["multi-MB D1 response + high rows read"]
  F["fix: skip the read on background rebuilds"] -.-> D
Loading

What caused this

Affected: Cloudflare account fdd120ce6acd80e0c9812d5fbc910db0 · D1 response bytes at 1.7Mbytes (baseline 268.1kbytes, 1.7σ above)

D1 response bytes (bytes)

Query that fired: Cloudflare · d1AnalyticsAdaptiveGroups.sum.queryBatchResponseBytes

Why this fix

D1 response bytes (queryBatchResponseBytes) on the Airbooks account's executor database rose from a 53,230–134,865 B per-bucket baseline to 3,809,396 B at 03:50Z on 2026-09-17, with the corroborating rows-read metric at 19,517 rows in the same bucket. The same three buckets (03:45–03:55Z) drive both checks, so this is one event; errors, refusals and resets stayed flat. The event recurs: hourly peaks of 2.7–15.1 MB with writeQueries ~70–150 (vs ~1 at baseline) across Sep 10–17.

The burst coincides, to the second, with executor tool sync preserved catalog warnings at 03:51:02Z for the cloudflare/cloudflareOrg and chroma_mcp/mattOmegaMarketsCom connections, both failing MCP connect with HTTP 401. That branch runs when resolveTools returns a non-authoritative listing: it keeps the persisted catalog and then materializes every tool row for the connection — input_schema/output_schema included — to return them. An explicit tool listing genuinely needs those rows, but the stale-catalog fan-out runs these as background rebuilds and discards the returned catalog, so the whole-catalog read is pure D1 work on that path.

{"type":"line","title":"D1 response bytes on the Airbooks executor database (2026-09-17)","xLabel":"Time (UTC)","yLabel":"Response bytes","unit":"B","series":[{"name":"D1 response bytes","data":[{"x":"2026-09-17T00:20:00Z","y":70577},{"x":"2026-09-17T00:25:00Z","y":134865},{"x":"2026-09-17T00:30:00Z","y":53230},{"x":"2026-09-17T02:10:00Z","y":70579},{"x":"2026-09-17T02:20:00Z","y":70271},{"x":"2026-09-17T03:15:00Z","y":64898},{"x":"2026-09-17T03:30:00Z","y":70279},{"x":"2026-09-17T03:45:00Z","y":202141},{"x":"2026-09-17T03:50:00Z","y":3809396},{"x":"2026-09-17T03:55:00Z","y":1716103}],"aggregate":{"label":"max","value":3809396}}],"markers":[{"x":"2026-09-17T03:51:00Z","label":"tool sync 401, catalog preserved","tone":"warning"}]}

The change skips the read when the in-flight sync is in background mode: the fan-out discards the result, and the catalog rows stay persisted, so nothing downstream changes. An explicit listing that joins the same in-flight sync flips its mode to explicit before the branch runs, so explicit callers still get the preserved rows and behavior is unchanged for them. The sync-time stamp and the executor tool sync preserved catalog warning are untouched, so operators keep the signal that a connection's upstream is broken.

Causal chain
  • Signal (metric): template.cf-d1-query-bytes-healthy — D1 response bytes 3,809,396 B at 03:50Z vs 53,230–134,865 B baseline (queryBatchResponseBytes); corroborating template.cf-d1-rows-read-healthy 19,517 rows in the same bucket
  • Surfacing site: cloudflare cloud account Airbooks (executor D1 database, ddc938f8-c295-4036-a588-8c28275e1856) at executor-cloudflare tool sync; packages/core/sdk/src/executor.ts#produceConnectionToolsUnshared
  • Mechanism: A tools read found the connections' persisted catalogs stale, so the fan-out ran background rebuilds; resolveTools returned a non-authoritative result (MCP 401), and the preserved-catalog branch materialized every tool row for the connection (input_schema/output_schema included) — a multi-MB D1 SELECT.
  • Producer: executor-cloudflare tool sync (background stale-catalog rebuild), instance connections cloudflareOrg (integration cloudflare) and mattOmegaMarketsCom (integration chroma_mcp), at packages/core/sdk/src/executor.ts#produceConnectionToolsUnshared
  • Trigger: a tools read at ~03:51Z marking the two MCP connections stale/expired, combined with both MCP servers answering HTTP 401
  • What happens to the failed unit today: The preserved-catalog branch stamps tools_synced_at, logs a warning, then reads and returns the kept catalog; the background fan-out discards the returned value (Effect.all(rebuilds) is unbound), so the read's only effect is D1 work. The rows stay persisted.
  • Cadence check: A per-reattempt full-catalog read on background rebuilds reproduces the recurring hourly peaks (2.7–15.1 MB, writeQueries ~70–150 vs ~1) seen across Sep 10–17; the 03:50Z spike is one instance.
  • Blast radius: 0 other resource(s), 0 other tenant(s); data at risk: none named
  • Producer evidence:
    • worker log 03:51:02.341Z/.503Z: executor tool sync preserved catalog, reason streamable-http HTTP 401, integrations cloudflare and chroma_mcp
    • d1AnalyticsAdaptiveGroups.sum.queryBatchResponseBytes 3,809,396 and rows_read 19,517 in the 03:50Z bucket
    • code read: preserved-catalog branch runs core.findMany("tool", { where }) and returns rows incl. schema columns
1 file changed (+7/-0)
  • packages/core/sdk/src/executor.ts: modified, +7/-0

View autofix View thread


Generated by Polylane. You can ask follow-ups by mentioning @polylane in a comment.

Co-authored-by: polylane[bot] <277585245+polylane[bot]@users.noreply.github.com>
@polylane polylane Bot added polylane severity:low Polylane autofix severity: low labels Sep 17, 2026
@polylane
polylane Bot requested a review from mhodgson September 17, 2026 04:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

polylane severity:low Polylane autofix severity: low

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant