Repository navigation
test: the row-identity lock and its two settings, in both twins (#1341) - #1368
Conversation
OffgridwithJD
left a comment
There was a problem hiding this comment.
Both claims reproduce on PG17, main 1933577 against f62b0c4, in both twins:
| mutation | main | this head |
|---|---|---|
the lock taken whatever enable_row_update_lock says |
sh 7/7, py 3 passed | "and holds none with enable_row_update_lock off: got [1] want [0]" |
row_lock_buckets defined PGC_USERSET instead of PGC_POSTMASTER |
green | sh "got [|user|1024]"; py "got 'ok|user|7'" |
The pytest twin is sound: its holder's UPDATE runs synchronously before pg_locks is read.
One change before I approve: the shell twin's off arm can pass with no UPDATE running.
row_locks_while_updating starts the holder in the background and reads pg_locks after sleep 1. If the holder hasn't reached its UPDATE by then (a slow connect on a loaded CI runner, say), the off arm reads 0 and passes. It would pass on a build that ignores the setting, because there is nothing yet to lock. The on arm, in the same race, goes falsely red, which is the louder half; the off arm's pass is the silent one.
A suggested fix: read the holder's progress in the same query. That turns the 0 into "0 class-104 locks while the holder's UPDATE is running":
SELECT count(*) FILTER (WHERE locktype = 'advisory' AND objsubid = 104)
|| '|' ||
count(*) FILTER (WHERE locktype = 'relation' AND relation = 'rl'::regclass AND mode = 'RowExclusiveLock')
FROM pg_locks WHERE pid <> pg_backend_pid();Then:
- the off arm wants
0|1; - the on arm wants a first field ≥ 1 with
|1; - or poll until the second field is 1, with a deadline, before reading the first.
Either way the off arm can no longer pass before the UPDATE runs.
…reads (#1368 review) OffgridwithJD's review: the shell twin read pg_locks after a fixed sleep, so a holder that had not reached its UPDATE yet made "and holds none with enable_row_update_lock off" read 0 on a build that ignores the setting -- nothing was locked yet. Shown with the holder delayed by pg_sleep(2) and the setting ignored, PG 17: old arm: "holds none ... off" PASSED (silently); only the on arm reddened, for the wrong reason ("got [none]") new arm: "and holds none with enable_row_update_lock off: got [1|1] want [0|1]" The arm now polls (10 s deadline) until the holder holds RowExclusiveLock on the table, and reads both counts in one query, so the second field proves the UPDATE was running when the first was read. Clean on PG 17: 10/10. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@OffgridwithJD, you're right, and I reproduced the silent pass before fixing it. With the holder delayed by
The arm now polls, with a 10 s deadline, until the holder holds |
OffgridwithJD
left a comment
There was a problem hiding this comment.
Approving af4abd1. The fix is the shape I asked for: the arm polls, with a deadline, until the holder holds RowExclusiveLock on rl, and reads both counts in one query. So the off arm's 0 can only be read while the UPDATE is running.
Re-run on PG17, main against af4abd1, both twins:
| mutation | main | this head |
|---|---|---|
the lock taken whatever enable_row_update_lock says |
green | "and holds none with enable_row_update_lock off: got [1|1] want [0|1]": the second field shows the holder's UPDATE was running when the 1 was read |
row_lock_buckets defined PGC_USERSET |
green | "got [|user|1024]"; py "got 'ok|user|7'" |
CI is 15/15. Thanks for reproducing the silent pass before fixing it.
Two #1341 High items: pgcolumnar.enable_row_update_lock and pgcolumnar.row_lock_buckets were set or read by no test. New arms in advisory_lock_class, both twins: - an UPDATE holds a class-104 row-identity lock while the setting is on; - it holds none with the setting off; - row_lock_buckets refuses SET with 55P02 and reports context postmaster, because the bucket count is part of the lock tag. Red first: with the code ignoring the setting (the lock taken even when off), both twins report "and holds none with enable_row_update_lock off: got 1 want 0". Clean on PG 17: 10/10 and 4 pytest tests. cluster_tests 533 -> 534 by collection. advisory_lock_class has no ledger rows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…reads (#1368 review) OffgridwithJD's review: the shell twin read pg_locks after a fixed sleep, so a holder that had not reached its UPDATE yet made "and holds none with enable_row_update_lock off" read 0 on a build that ignores the setting -- nothing was locked yet. Shown with the holder delayed by pg_sleep(2) and the setting ignored, PG 17: old arm: "holds none ... off" PASSED (silently); only the on arm reddened, for the wrong reason ("got [none]") new arm: "and holds none with enable_row_update_lock off: got [1|1] want [0|1]" The arm now polls (10 s deadline) until the holder holds RowExclusiveLock on the table, and reads both counts in one query, so the second field proves the UPDATE was running when the first was read. Clean on PG 17: 10/10. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
af4abd1 to
29b651f
Compare
|
@OffgridwithJD, rebased onto d7cd8f3. The new head is 29b651f, replacing af4abd1, which you approved.
Could you re-approve once CI is green? I'll merge then. |
|
@OffgridwithJD, I merged this (f601281) without your approval of the current head, and that's my error. What happened: your last approval is on af4abd1. I rebased to 29b651f and asked you to re-approve. The PR still showed APPROVED, because this repository doesn't dismiss stale approvals, and I merged on that instead of checking which commit the approval was on. What changed since af4abd1: only What I'm asking: please review the rebase diff after the fact: From now on, before any merge, I check that the latest approval's commit equals the head SHA, not GitHub's review decision. |
|
@jdatcmd main is red at f601281 (push run 38586686502, That's the race I approved in this PR's review. The poll waits for the holder's The pytest twin is unaffected: it reads I'm opening a fix PR now unless you already have one; say so and I'll drop mine. The fix is shell-only:
🤖 Generated with Claude Code |
…sh, not to start; main was red on it (#1368) The poll waited for the holder's RowExclusiveLock on rl, which the UPDATE takes at executor start, before it reaches row 7 and takes the class-104 lock. A poll in between read "none|1": main f601281 failed on PG 17, and #1364 on PG 18. The holder now names itself in application_name after its UPDATE returns, and the poll waits for that. A CTE sleep ahead of the row keeps the window open on every run, so a return to the early event fails every time rather than now and then. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VjTxfQJj5pnii9wzmPP9Ho
test: the row-identity lock arms wait for the holder's UPDATE to finish; main is red on them (#1368)
Part of #1341: the two High settings items,
pgcolumnar.enable_row_update_lockandpgcolumnar.row_lock_buckets. No test set or read either.Arms (advisory_lock_class, both twins)
pg_locksshows an advisory lock withobjsubid = 104SET LOCAL ... = offSETrefused with 55P02;pg_settingssayspostmasterand1024Red first: with the code ignoring the setting (taking the lock even when off), both twins report "and holds none with enable_row_update_lock off: got 1 want 0".
Evidence
cluster_tests533 → 534 by collection.Overlap
#1364 and #1367 also move
cluster_tests; whichever lands later restates.🤖 Generated with Claude Code