Repository navigation
Conversation
…ate change to a look-alike row The generator skipped the main record's table in the whole-database frame for an update (and kept it append-only for a create), leaving it to the contract's own read. The recheck reads no surface, so a late write to any row of that table, such as a look-alike record changed after the verdict, passed the recheck. frame.database.judged_by names a table a surface reads in full. After the save, the rows that surface saw move are left to it (the target, or the rows the write adds, with only their declared fields); every other row is compared by the database frame. A recheck compares the whole table. The database is now read before the surfaces, so no change falls between the two reads.
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.
Item 2 of #1677. This is a fail-open fix, so it ships alone.
The fail-open
The held-out test found a late write that the recheck missed. A retried request checked out a look-alike record about two minutes after the verdict. The recheck read the database 300 s after the write and said CONFIRMED.
Cause: for an update, the generator gave the main record's table (and the tables of rows that hold values)
allow_tables: {mode: skip}in the whole-database frame, and left them to the contract's own read. After the save that read judges every row correctly: only the declared target may change, and only in its excused columns. A recheck reads no surface, though, only the database. So at the recheck a skipped table was not read at all, and a change to any row of it passed, including a change to the excused columns on a row the write never declared. For a create, the same tables were append-only, so a late look-alike added at the recheck passed too.The fix
frame.database.judged_by(new,runtime/effects/frame.py): table -> a surface that reads every row and every column of it. After the save, the rows the surface saw change, appear or go (matched by primary key) are left to that surface, which judges them with the effects' selectors andtarget_fields_by_surface. Every other row of the table is compared by the database frame. A recheck reads no surface, so there the whole table is compared and any change since the post-save read refutes.judged_bytable can't also be inallow_tablesorviews, and must name a real surface.judge_run_framereads the database frame before the surfaces. A change can't then land between the surface read and the settled read that a recheck compares with.judged_byfor the main record's table and the tables of rows that hold values, for every flavor, in place ofskipor append-only. An excused column is excused only on the rows the write declares (the target, or the rows it adds).Docs:
VERIFY_RUN.md(the field),GENERATE_CONTRACTS.md(the frame), CAPABILITIES.md.Tests
tests/test_frame_judged_by.py(SQLite, public API): a correct checkout holds and its recheck confirms. A look-alike checked out in the window refutes. A look-alike checked out after the verdict refutes at the recheck. A late second change to the target refutes at the recheck. A surface that misses a column leaves the table to the frame (refuted). The same contract withoutjudged_byrefutes. Load-time refusals for an unknown surface and for an allow-listed table.tests/test_generate_verify_contract.py(real PostgreSQL, generated contracts): every generated contract compares its own tables. A late update of the look-alike issue, a late status change on the look-alike only, and a late second change to the target are each REFUTED at the recheck. A late duplicate create is REFUTED at the recheck. A correct create and a correct update with nothing late are CONFIRMED at the recheck.Real-app evidence
Running on a held-out app (instance 1) once the shared VM has room; this section will hold the commands and outputs.