Repository navigation
fix(storage): bound projection retries and cut v0.40.12 - #535
Open
raphaelmansuy wants to merge 25 commits into
Open
raphaelmansuy wants to merge 25 commits into
raphaelmansuy wants to merge 25 commits into
Conversation
Migration 220 put a restrictive clearance predicate on eight tables. Two terms of that predicate are the same value for the whole statement (break glass, and whether the workspace is enforcing), but they sat inside the per-row clearance_covers() function, so the planner called the function for every candidate row and a scalar subquery could not be hoisted out of it. Split the predicate where row-independence ends: clearance_bypass() holds the row-independent half, clearance_rank_covers() keeps rank and compartment coverage, and clearance_covers() stays as bypass OR rank_covers so the out-of-policy callers in graph/plain keep working unchanged. Policies call the bypass as (SELECT clearance_bypass()), which the planner turns into an InitPlan — the same technique migration 208 applied to current_tenant_id(). Migration 229 gives the (tenant_id, workspace_id) indexes on chunks and documents the clearance columns as INCLUDE payload, so the policy is answered from the index instead of a heap fetch per candidate row. Each index replaces the existing one, so no table gains an index to maintain. Two byte-identical duplicate index pairs are dropped. Measured as edgequake_tenant_access via SET LOCAL ROLE (the path rls::with_rls_transaction takes), 120k chunks / 36k in one workspace: chunk count 598 ms -> 6 ms chunk FTS page 267 ms -> 107 ms document page 13 ms -> 0.9 ms document count 12 ms -> 0.3 ms Equivalence is proven exhaustively over every combination of enforcement, break-glass, rank, allow-empty, compartments and workspace settings: 19,440 comparisons, 0 mismatches, against the verbatim migration-220 definition. e2e_spec228 additionally fails if the bypass is not hoisted. Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
Adds a live-Postgres e2e test that compares the split predicate against clearance_covers() across every combination of enforcement, break-glass, rank, allow-empty, compartments and workspace settings, and asserts the planner hoists the bypass (InitPlan `$n` before the per-row `SubPlan`) and reaches the chunks scope index as an index-only scan. Keeps the HEAD schema train contracts current: 229 is the head, the release map covers 226 through 229, and two new contracts pin the shape of migrations 228 and 229. Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
Migration 208 wrapped session helpers in `(SELECT fn())` inside policy expressions and documented the invariant: "New policies must use the wrapped form ... e2e_rls_initplan_policies (edgequake-storage) fails if any are left bare." That guard test was never written, and five later migrations reintroduced the bare form — 215, 216, 219, 222 and 225 — so six policies still call the helpers once per row. A bare STABLE helper in a policy becomes a Filter the executor re-evaluates for every emitted row. The plans show it: the index-only scan carries `Filter: $0` over 4,031 rows on workspace_term_stats and 372,000 on chunk_terms. Wrapped, both scans carry no filter and the helper is a single InitPlan. Measured as edgequake_tenant_access after SET LOCAL ROLE, 120k chunks and 2.2M chunk_terms rows, median of 9 alternating runs on an otherwise identical pair of databases: full BM25 statement 24.6 ms -> 12.4 ms (9 of 9 runs improved) The composite statement gains what an isolated count(*) does not, because a count reads only the index while BM25 joins both tables and re-scans chunk_terms three times. Meaning is unchanged: app.current_* are fixed for the statement, so the wrapped call returns what the bare call returned. Verified on the three states that could differ — unset, set and malformed GUC — and the rewrite aborts if unwrapping does not reproduce the original expression. Adds the guard test 208 promised, so this cannot rot again. Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
e2e_rls_initplan_policies fails when any policy expression still calls a session helper bare, naming the table, policy and clause. It fails on a database before migration 230 and passes after — verified both directions. Also pins 230 in the HEAD train contracts and the release map, and records the fix in docs/migrations.md. Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
Six contract tests asserted source-text literals that the code has since outgrown. All six failed on edgequake-main before this branch existed, so they were dead weight rather than a signal; each was confirmed unrelated to the migrations here (no production source changed in this commit). Each is realigned to the design now in place, keeping the invariant the test existed to defend: - incident edges: assert the OR template that replaced "OR ag_catalog"; the EDGE child table and no-UNION rules are unchanged. - hot node reads: IMP-031 moved node reads off Cypher property MATCH onto native SQL (cypher_query_bound is now fallback-only); assert that. - eq_* DDL early exit: assert the O(1) catalog probe short-circuits before any DDL, rather than one reworded log line. - eq_* sync triggers: they moved to helpers::sync_triggers and use CREATE OR REPLACE, so assert no DROP TRIGGER on the hot path instead. - discovery: migration 207 moved the GIN-eligible probe into a SECURITY DEFINER lookup; assert ?| plus the unnest/generate_series probe CTE there. - vector DDL GUCs and PreferExisting: both were refactored into helpers and the provider; assert the behaviour at its new home. edgequake-storage now passes 180/180 suites, up from 173 with 6 failing. Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
Migration Guard / Layer 1 failed on this branch: check_epoch_coverage.sh compares the [unreleased] pin against HEAD's numbered-migration set, and the pin still described schema 227 / 225 files while HEAD is 230 / 228. unreleased pin 7491afd4e3a729e7 != HEAD db2868e7660eaf55 (max=230 files=228) Repin to the measured HEAD hash. `./scripts/check_epoch_coverage.sh` now reports "HEAD schema set: max=230 n=228 hash=db2868e7660eaf55 [OK unreleased]" and every published tag still resolves to an epoch. Nothing here is published-tag metadata: no [[epoch]] entry changes, so the v0.40.x history keeps its own hash. Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
Migration Guard / Layer 1's "Schema train docs parity" step failed: the check compares the highest numbered migration (230) against manifest, then requires the number to appear in docs/operations/upgrading.md, edgequake/docs/migrations.md and edgequake/migrations/NOTES.md. upgrading.md and NOTES.md still stopped at 227, and the per-migration table in upgrading.md had no rows for 228-230. Adds the three rows and refreshes both train headers. `./scripts/check_schema_train_docs.sh` passes. Also states plainly that 228-230 are unreleased — no [[epoch]] entry is added here, so the v0.40.x published-tag history keeps its own hash. Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
A Vertex connection saves its Google Cloud project. Queries, chat, and gemini-embedding-2 use that project, so a server without GOOGLE_CLOUD_PROJECT can still answer. Schema moves 227 to 231 (migrations 228-231). Row-security InitPlans from 228-230 ship in this cut. SPEC-001 Acc is not re-scored. Mix retrieval is unchanged. Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
Deployment Providers can edit a provider. Update saves the name, base URL, key, and the Google Cloud project for Vertex. Schema stays 231. Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
The v0.40.9 image publish was cancelled when quality-gates hit 90 minutes on a cold compile. Raise that job limit to 180 minutes so the same provider Edit can publish. Schema stays 231. Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
The organization ceiling and the workspace list were a grid of empty boxes. An empty box meant the job was open, which reads as off. Each model now lists only the jobs it can do, grouped by provider, and a job stays open until the first model is allowed. Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
A Normal or image query built gemini-embedding-2 from the process environment and ignored the project stored on the Vertex connection. Query, chat, and image ingest now open that connection. Schema stays 231. Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
Carry forward the retry ceiling from #534 and close the gaps a review found: - The last allowed attempt (attempt == max) was quarantined before its apply call, so a delivery that would succeed on attempt max was retired without ever being tried. Split the two boundaries: over budget (attempt > max) quarantines on sight, exhausted (attempt >= max) quarantines only after a failure. - A one-delivery batch re-applied its exhausted item after the batch call had already failed. That pays the same expensive failing provider call (the timeout/OOM path in #511) twice for no discriminating value. Only multi-delivery batches isolate now. - Move the retry policy into small helpers (attempts_over_budget, attempts_exhausted, backoff_ms, renew_for, count_apply_call, release_retry, quarantine_exhausted, retry_failed_batch, isolate_exhausted) instead of one 130-line match arm. - Add EDGEQUAKE_PROJECTION_MAX_ATTEMPTS so operators can tune or disable the ceiling (0 keeps the pre-fix unbounded retries). Tests cover the ceiling boundaries, batch isolation keeping a healthy sibling alive, unbounded mode, and env parsing. Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
Operators need to know a delivery now stops at 10 claims (0 disables the ceiling) and how to re-drive one after fixing the cause: the reason lives in projection_deliveries.receipt, and resetting attempts is the recovery. Register EDGEQUAKE_PROJECTION_MAX_ATTEMPTS so the env gates stay honest. Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
Projection deliveries stop after 10 claimed attempts. A failed batch quarantines only the delivery that still fails on its own, and a one-delivery batch does not pay that call twice. Schema stays 231, so edgequake migrate is a no-op on a database already at 231. SPEC-001 Acc is not re-scored. Mix retrieval is unchanged. Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
The saved-connection probe inserts google_project, so the SPEC-163 role database has to add that column. cargo audit now ignores RUSTSEC-2026-0429, the same ZipArchive::extract abort already accepted for zip 2 and zip 3. The UI gate stays on Ubuntu 24.04 because Playwright 1.58 has no chromium build for ubuntu-latest's 26.04 image. Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
…screens The key field is labeled optional unless the shape requires one. An untested fleet provider explains itself and does not offer a model id. Connection saves go to /connections?scope=deployment, and the picture suggestion is read after the corpus query resolves. Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
pdfium-auto 0.3.1 downloads chromium%2F7961. GitHub answers that URL, and the slash-form HTML download, with HTTP 504, and the build script aborts. CI and the release image now fetch the chromium/7961 asset through the release API and set PDFIUM_BUNDLE_LIB. Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
oven-sh/setup-bun downloads the HTML release URL, which returns HTTP 504. The UI gate never calls bun, so that step is removed. Jobs that run bun install fetch the latest release through the asset API. Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
Playwright starts the UI gate with bun run, and release_gates.sh typechecks with bunx. The asset install only copied the bun binary, so bunx was missing and the UI gate had no bun at all. Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
A workflow token is rejected with HTTP 403 on another repo's release asset, and echo "VAR=$(download)" hides that failure and exports an empty path. pdfium is fetched without the token. Swagger UI is fetched from codeload, which serves the tag zip the github.com archive URL answers 504 for. A failed download now fails the step. Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
GitHub answers the pdfium asset API and the Swagger UI archive with HTTP 502, 503, and 504. CI and the release image now use the chromium/7961 linux archives and Swagger UI v5.17.14 checked into the tree. Co-authored-by: Raphael MANSUY <raphael.mansuy@gmail.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Lands the projection retry ceiling from #534 on top of the published v0.40.11 line and cuts v0.40.12. Merging this fast-forwards
edgequake-mainthrough the already-shipped v0.40.8–v0.40.11 commits (schema 231). Squash-merging would drop that schema and must not be used.#534 itself stays on the contributor fork, which refuses a push that updates
.github/workflows(the app token has noworkflowspermission). Rebasing that head onto v0.40.11 is therefore impossible from here. Merging #534 as it stands would base the fix on v0.40.7 (schema 227) and regress every release since v0.40.8.The fix
projection_deliveries.attemptsincrements when a row is claimed. The worker used to retry a failing apply forever, which is how a dense document (#511) loops until the process runs out of memory.EDGEQUAKE_PROJECTION_MAX_ATTEMPTS).0keeps the old unbounded behavior. Unset or unparsable keeps the default.projection_deliveries.receipt(bytea). Recovery is a SQL reset topendingwithattempts = 0. A quarantineddocument_batchdelivery keeps that document's serving fence closed.#522 (duplicate endpoint ids) is a separate change and is not in this cut.
Release
edgequake migrateis a no-op.ba0c9db9d18d39c1.medical-mid-20261024T031725Zstays the attestation: EdgeQuake 0.8084, LightRAG 0.7681, delta +0.0403, 95% CI [-0.0241, +0.1036], n_paired 72, a statistical tie.CI
edc40a9edfixed the three gates this cut exposed:google_projectcolumn the saved-connection insert writes.RUSTSEC-2026-0429, the sameZipArchive::extractabort already ignored as 0367/0368/0369.edgeparse-core0.3.2 requireszip2 and only writes archives.utoipa-swagger-ui9 useszip3 at build time and copies the upstream Swagger UI archive. Neither callsextract()on untrusted input.ubuntu-24.04because Playwright 1.58 has no Chromium build forubuntu26.04-x64.That run then executed the suite (390 passed) and failed six tests that v0.40.11 never reached, because its install died on Ubuntu 26.04.
82e24ee91aligns those tests with the screens:requires_key./connections?scope=deployment, so the tenant-header assertion has to match that URL.5b9ad3bbastops the compile jobs dying on pdfium.pdfium-auto0.3.1 downloadschromium%2F7961. GitHub answers that URL, and the slash-form HTML download, with HTTP 504, and the build script aborts. CI and the release image now fetch the chromium/7961 asset through the release API and setPDFIUM_BUNDLE_LIB.Layer 1 on
pull_request_targetstill fails, and this pull request cannot make that job green. That event checks outedgequake-main(v0.40.7, schema 227), so tags v0.40.8–v0.40.11 look uncovered. The same Layer 1 job on thepull_requestevent, which checks out this head, passed. Thepull_request_targetLayer 2 job also compiles that stale base, so it can still die on the same pdfium 504 until the merge updates the base. Merging repairs both.Verification
cargo test -p edgequake-storage --features postgres --lib --locked— 632 passedcargo fmt --checkand storage clippy-D warningscleanlibpdfium.so(ELF x86-64) through the release-asset API from this environment