Repository navigation
TC-735: persist KV blobs before the per-space sequence lock - #301
Conversation
|
Correction on current head |
|
REQUEST_CHANGES: see TC-735 |
samgbafa
left a comment
There was a problem hiding this comment.
REQUEST_CHANGES — TC-735
|
REQUEST_CHANGES: see TC-735 |
samgbafa
left a comment
There was a problem hiding this comment.
REQUEST_CHANGES — TC-735
|
REQUEST_CHANGES: see TC-735 |
samgbafa
left a comment
There was a problem hiding this comment.
REQUEST_CHANGES — TC-735
|
REQUEST_CHANGES: see TC-735 |
samgbafa
left a comment
There was a problem hiding this comment.
REQUEST_CHANGES — TC-735
|
REQUEST_CHANGES: see TC-735 |
samgbafa
left a comment
There was a problem hiding this comment.
REQUEST_CHANGES — TC-735
|
APPROVE: see TC-735 |
|
REQUEST_CHANGES: see TC-735 |
samgbafa
left a comment
There was a problem hiding this comment.
REQUEST_CHANGES — TC-735
|
APPROVE: see TC-735 |
samgbafa
left a comment
There was a problem hiding this comment.
REQUEST_CHANGES — TC-735
|
APPROVE: see TC-735 |
Brings main a735c4d (TC-1071 CID-alias revocations, TC-1176 boot lock_timeout, request telemetry) and #302 580abe5, whose batched auth snapshot resolves alias revocations in its one statement. Startup keeps the pre-S3-listing migration (now main's lock_timeout- bounded migrate) so deleting fences are read before the quota listing, then opens the database through main's new_with_encryption, which finds migrations current and runs the CID-alias backfill with the column key before serving requests.
TC-735's later kv_orphan_candidates migration made Migrator::down(1) undo it instead of the cursor index. Identical to #341's fix.
#337 fixed S3 remove sizing with HeadObject; this branch already sizes and deletes through HeadObject with generation keys, operation locks and confirmed-absence refunds, so keep its remove and drop main's now-unused helpers. Main's TC-1354 MinIO test uses generation 0 (the bare key).
Rename the unshipped kv_orphan_candidates migration from m20261015_020000 to m20261017_020000 so it sorts after main's m20261017_000000_epoch_order_space_index, per the second-merger rule. Take main's cursor-index lock test (migrated_through), dropping ours.
CI aborted policy_owner_may_be_a_session_the_space_owner_delegated_to on the 2 MiB test stack. gdb at the overflow: the deepest path is invoke -> boxed commit block -> transact -> process_admitted -> save. transact's debug frame (~188 KB) and the test's own frame (~936 KB) are the same as main. This branch adds the boxed commit block's frame (106,608 bytes) under the outer invoke frame. In debug builds every awaited temporary in that block has its own stack slot. Move the upload loop and the post-transact reads into their own methods, so their temporaries are on the stack only while they run. The block's frame is now 78,208 bytes. The test's minimum passing stack falls from 2,056 KiB to 2,028 KiB (main: 2,032 KiB). The test stack is unchanged.
# Conflicts: # CHANGELOG.md
Pre-upload authorization is one read statement and registration one write statement that also checks every invocation space exists, replacing the preparatory BEGIN/COMMIT and the separate hosted-space COUNT. Other backends keep the preparatory transaction. Add round-trip rows for multi-blob and shared-blob multipart puts; their budgets are placeholders until measured.
# Conflicts: # CHANGELOG.md
Route the PostgreSQL pre-upload authorization reads and the idempotent registration statement through the pool's connection-loss recovery, as the preparatory BEGIN was, without marking a user-visible write.
# Conflicts: # CHANGELOG.md
…cking Set the KV put budget to the measured 41 and add exact rows for a two-blob and a one-blob two-key multipart put. Cleanup of candidate metadata after a failed write no longer marks the request as having possibly applied a write, so a failure before the KV transaction began stays retryable.
The pool classifies the authorization snapshot's multi-CTE statement as a possible write, which made a failure before any KV transaction unknown-outcome. Run those reads through a view that retries once after a lost connection and does not count as a write.
The PostgreSQL autocommit registration applies only to at most one distinct blob; several blobs share one preparatory transaction that commits every claim or none. The registration statement is a write: it is not retried and a lost connection around it is an unknown outcome. The authorization view accepts only pure reads.
…d-space check on PostgreSQL The PostgreSQL autocommit registration cannot absorb a stale pooled connection without retrying a write, which the pool's recovery tests rely on. Authorization and every blob's claim share one transaction again, committing all claims or none; each PostgreSQL claim statement also checks the invocation's spaces are hosted, replacing the separate COUNT. The multi-write budget rows keep the ceiling below the expected count until measured.
…ion; recovery notes The collector checks references before removing a blob and closes a deleting fence whose blob a committed write references. The claim-table migration uses if-not-exists on every table and index. Add tests for both, for all-or-nothing two-blob registration and for the bounded preparatory BEGIN retry; pin the measured round-trip ceilings; document recovery as a forward fix. Details: TC-735
…nvoke level all-or-nothing tests Document the recovery script, the seven round trips of a put, and the retryable classification of a lost connection during the preparatory transaction. Add an invoke-level test and a route-level test that a two-blob put with a pending deletion claims nothing. Details: TC-735
|
APPROVE: see TC-735 (reviewed exact head e54bf89) |
Change
KV puts persist content-addressed blobs before the per-space sequence lock. A preparatory transaction authorizes the invocation, checks that every space it names is hosted, and claims each distinct blob under a durable attempt; it commits all claims or none. Uploads then hold no pooled database connection, transaction or lock. The commit transaction takes the sequence guard, verifies its own claim, re-authorizes, checks preconditions, assigns the sequence and commits the reference, so
kv/synccursors stay commit-ordered.(space, content hash, generation)fence plus one claim per writer. The last unreferenced claim moves the fence todeleting; no writer can deduplicate or upload over it until physical absence is confirmed. Known failures refund once after deletion.hash@N.(space, hash, generation)serialization of physical operations; filesystem staging publishes complete files atomically; S3 startup rebuilds pending refunds from the same listing as the quota count. Quota stays conservative and per process.m20261023_010000_kv_orphan_candidatesadds thegenerationcolumns and three claim tables withif_not_exists, so it can be applied again over its own tables on PostgreSQL and SQLite (MySQL index creation has no such guard).unavailable_retryable; the claims are idempotent per attempt ID and hold only candidate metadata.Round trips (PostgreSQL, measured in CI)
sign-in 71, kv put 42, kv get 16, batch get 16, batch head 16, two-blob put 48, one-blob two-key put 45. The ceilings in
postgres_round_trip_budget.rsequal these. The hosted-space check is part of each claim statement, so no separate query runs on a put with a blob.Rollback
Recovery after deployment is a forward fix; an image rollback alone cannot boot, and a binary without this change is not a supported rollback target (
docs/kv-sync.md, "Recovery after deploying generation-keyed blobs"). Take a named full database backup before deploying. The document holds thecurrent_kv.generationrepair statement and the self-aborting emergency script for the case where no record has a generation above zero.Tests
New: all-or-nothing two-blob registration (registration level, invoke level and route level), a deleting fence over a referenced blob, migration re-run over populated tables (SQLite and PostgreSQL), bounded preparatory
BEGINretry with no retry after a transaction statement, multi-write round-trip rows. PostgreSQL-backed tests run in CI.Details: TC-735