Repository navigation
docs(f3): a row lock the shipped database does not take - #117
Merged
Merged
Conversation
F3-R11 required a recovery code to be "matched and consumed atomically under a row lock". SQLite has no row lock. Laravel's SQLite grammar compiles lockForUpdate() to an empty string, so the call that appeared to satisfy the requirement emitted no SQL at all, and the requirement named a mechanism the shipped database does not implement. What made that worth changing rather than leaving decorative is the cost. F3-R13 requires a constant mismatch cost, which is ten bcrypt comparisons per attempt -- 3.5 seconds measured at rounds 12. The connection runs transaction_mode IMMEDIATE, taking the database-wide write lock at BEGIN, and both shells serve one request at a time. So a transaction held across the hashing freezes the whole application for three and a half seconds, from an endpoint reachable without any credential. The requirement now states the property instead of a mechanism: spend exactly one code, never spend a spent one, and keep the hashing out of the transaction that spends it. Atomicity is carried by a compare-and- set whose affected-row count is the answer, which needs no row lock and cannot be interleaved. Spec: F3-R11, F3-R13, GOV-R27 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Wessel Verheij <info@nightworks.io>
|
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.



F3-R11required a recovery code to be "matched and consumed atomically under a row lock".SQLite has no row lock. Laravel's
SQLiteGrammar::compileLock()returns'', so the call that appeared to satisfy the requirement emits no SQL:The requirement named a mechanism the shipped database does not implement, and the implementation's
lockForUpdate()was decorative.Why that was worth changing rather than leaving
F3-R13requires a constant mismatch cost, which the implementation delivers as a fixed ten bcrypt comparisons per attempt — 3.5 seconds, measured at rounds 12 on this hardware.The connection runs
transaction_mode = IMMEDIATE, which takes the database-wide write lock atBEGIN, and both shells serve one request at a time. A transaction held across that hashing therefore freezes the entire application for three and a half seconds — from an endpoint that needs no credential to reach.So the decorative lock was hiding a real constraint: the expensive comparison must not happen inside the consuming transaction.
What it says now
The property, not a mechanism. Atomicity is carried by a compare-and-set — a single
UPDATE … WHERE id = ? AND used_at IS NULLwhose affected-row count is the answer — which needs no row lock and cannot be interleaved.A new section, A row lock the shipped database does not take, records the measurement and the reasoning so the next reader does not reinstate the call.
Ordering
Filed before the implementing change, per
GOV-R27. The beatrax PR that moves the hashing out of the transaction will citeF3-R11and will not merge before this does.Gates run locally:
integrity.pyclean,manifest_check.pyclean (1 manifest, 373 goals),markdownlint-cli20 issues.Spec: F3-R11, F3-R13, GOV-R27