Repository navigation
fix(engine): nested registry scopes, double SERVICE fetch, budget-truncated streams, LOAD blank nodes - #6655
Conversation
functions::Guard, aggregates::Guard and spatial::Guard cleared the thread-local on drop, so a nested with_functions / with_aggregates / with_spatial_index scope unregistered the outer one for the rest of the outer scope. Store the replaced value at install and restore it on drop, matching local_services::Guard and the WorkerGuards. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C1gMf8RJim1LzuKapZNpZR
When the left of a Join was a SERVICE and the bind-join pushdown declined, the left was evaluated eagerly and then again on the fall-through, fetching the endpoint (or running a local handler) twice. Evaluate the right first and the SERVICE at most once. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C1gMf8RJim1LzuKapZNpZR
…nclosed (#4239) The single-pattern scan fast path appended the closing ]}} after a budget::exhausted break, so the emit sink saw a complete-looking but truncated document before the caller reported the abort. Skip the terminator when the budget tripped, on the serial and parallel paths. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C1gMf8RJim1LzuKapZNpZR
…4158) The budget break sat in the inner per-member loop, so an exhausted budget only ended the current correlation group and the next group's seeded B' was still dispatched. Break out of the outer group loop instead, so the SIP strategy stops the same way the hash strategy does. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C1gMf8RJim1LzuKapZNpZR
LOAD extended the destination with the document's blank-node labels verbatim, so two documents (or a document and the store) that both used _:b0 shared one node, and re-LOADing a document was idempotent. LOAD is an RDF merge (SPARQL 1.1 Update 3.1.5): freshen the document's blank nodes once per LOAD, keeping one node per label within the document. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C1gMf8RJim1LzuKapZNpZR
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C1gMf8RJim1LzuKapZNpZR
🔎 Codex reviewer —
|
…prefix A bare fbN counter restarts at 0 per process, so a fresh node could reuse a label already in the store and merge with it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C1gMf8RJim1LzuKapZNpZR
|
Fixed in 588835d, at the root rather than only for LOAD. Fresh labels now carry a random per-process prefix ( Generated by Claude Code |
🔎 Codex reviewer —
|
…budget per emitted JSON chunk - FreshBnodes uses oxrdf's random BlankNode::default() (the browser getrandom backend is configured) instead of SystemTime/process::id, which trap on wasm32-unknown-unknown - the SELECT-JSON fast path rechecks the budget after each emitted chunk and right before the terminator, so a cancellation raised mid-stream never closes the document Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C1gMf8RJim1LzuKapZNpZR
|
Both findings are fixed in 648f390.
Generated by Claude Code |
🔎 Codex reviewer —
|
Requested by Jesse · project thread
Before: five engine bugs from the backlog.
with_functions,with_aggregatesorwith_spatial_indexscope unregistered the outer scope's registry for the rest of the outer scope.SERVICEon the left of a join hit the endpoint twice (or ran a local handler twice) whenever the bind-join pushdown declined.]}}before the error was reported.LOADreused the document's blank-node labels, so two documents (or a document and the store) that both used_:b0shared one node.After: nested scopes restore the outer registry, the SERVICE is evaluated once, a truncated stream stays unclosed, the anti-join stops on the first exhaustion, and
LOADstandardises blank nodes apart as an RDF merge requires (SPARQL 1.1 Update §3.1.5).Closes #4467, closes #4438, closes #4239, closes #4158, closes #4160.
How. One commit per issue.
local_services::Guardand theWorkerGuards already do.budget::exhaustedis set.load_documentfreshens each document's blank nodes through the existingFreshBnodes.Each fix has a regression test that fails on main, except #4158, which is control flow only and is covered by the existing theta anti-join suites.
The changes sit in always-compiled engine code, so
bench/feature-off-declarations/<this PR>.jsonis included.Benchmark
Local
sparq-cli bench, operators suite (2,000 entities, 16k triples), release-fast binaries of main0268a638f2and this PR. Best of 5 iterations per round, minimum over 5 interleaved rounds; row counts match on every query. Every query is within noise of main; the geomean ratio is 0.993.🤖 Generated with Claude Code
https://claude.ai/code/session_01C1gMf8RJim1LzuKapZNpZR
Generated by Claude Code
Generated by Claude Code