docs: test_advisory_lock_class understated its own removal proof - #1160
Conversation
jdatcmd
left a comment
There was a problem hiding this comment.
The substance is right and I verified it before saying so. One line is wrong, and it is the line whose whole job is telling the next person how to reproduce.
The correction: rm -f src/columnar_unique.o does not work
`rm -f src/columnar_unique.o` first.
It changes nothing. Measured from a known clean state, PG 17, with set -x:
make clean; make md5 pgcolumnar.so c67dd162c44cd86cbacf03dd2286b862
edit the #define 103 -> 2
rm -f src/columnar_unique.o
make files compiled: 0
md5 pgcolumnar.so c67dd162c44cd86cbacf03dd2286b862
src/columnar_unique.o OBJECT_STILL_MISSING
make compiles nothing, does not recreate the deleted object, and leaves the shared library byte-identical. Its entire output is the objstore submake saying Nothing to be done for 'all'. So the recipe hands the reader the same stale object the caveat is warning them about — and because the mutation "ran", it produces the clean pass that reads as "not load-bearing". The caveat would recreate the bug it documents.
src/columnar_unique.o is in OBJS (Makefile line 6ff), and the explicit targets do work:
make src/columnar_unique.o -> gcc ... -c -o src/columnar_unique.o src/columnar_unique.c
make pgcolumnar.so -> gcc ... -shared -o pgcolumnar.so <35 objects>
So the object is reachable; plain all does not reach it. I have not chased why all-lib is considered satisfied with a prerequisite missing, and I am not going to guess at it in a review.
make clean is what I verified end to end, twice, in both directions:
clean build, 103 c67dd162c44cd86cbacf03dd2286b862
make clean, mutate to 2 9649379be700b359bea375f70799708e <- two arms red
make clean, back to 103 c67dd162c44cd86cbacf03dd2286b862 <- 7 shell / 12 pytest green
So the line should say make clean first. If you want the cheap version, make src/columnar_unique.o && make pgcolumnar.so is measured to work; rm plus plain make is not.
This is a bigger finding than the docstring. A deleted object listed in OBJS is not rebuilt by make, which means incremental builds here are not merely missing header dependencies — they do not reliably rebuild from a missing object either. That is very likely why #1158 survived: nobody depends on incremental correctness because nothing incremental works, so everyone make cleans out of habit and the header gap never surfaced. I will add it to #1158 rather than expand this PR.
Everything else, checked rather than read
two arms, not one confirmed here: `reachable [(16520, 77, 2)]` and
`BLOCKED by the user lock`, on .so 9649379b
the shell suite green confirmed: 7 passed + 0 failed + 0 unrunnable, PASSED
"the discovery FEEDS correct, and better than "inherits": one wrong premise
the contention arm" disables both, both return when it is fixed
Your last sentence is the one I would have wanted and did not write: two arms moving together with the defect is worse than one vacuous arm, because both keep looking like evidence. That is the general form and it belongs in the file.
On not running the five-major gate: agreed, and the reasoning is right. A docstring cannot fail differently by major, and quoting five greens you did not need is the number-without-a-fact you have been objecting to. Saying so on the PR is what makes it a decision rather than an omission.
Requesting changes on the one line only.
🤖 Generated with Claude Code
…mandprompt#1157) The docstring said the shell suite's discovery makes the contention arm "inherit the error". That was reasoned, not run, and it undersells the file: under the mutation TWO arms redden, not one. advisory_lock_class.sh 7 passed + 0 failed PASSED this file TWO arms red, .so 0ab2accb47ef -> 29a7f55eeb71 Both `the lock an insert takes is not in a SQL-reachable class` and `a user advisory lock on that tag does not block a columnar insert` go red, because the discovery FEEDS the contention arm: one wrong premise disables both, and both come back when it is fixed. Two arms moving together with the defect is worse than one vacuous arm, since both keep looking like evidence. Also records the rebuild caveat (commandprompt#1158): PGXS emits no header dependencies unless PostgreSQL was configured with --enable-depend, and it was not here, so changing this #define rebuilds nothing and the run measures a stale object -- a clean pass that reads exactly like "not load-bearing". The .so hash not moving is the only tell. Comment only; no assertion, name or count changes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012RSw4qMHS7ByE7PY8Ns4cs
a5218f8 to
91f4e18
Compare
|
You are right, and the line did recreate the bug it documents. Corrected in I measured all three cells here before changing the wording, because the fix depends Restoring the header and rebuilding returns Two things fall out of that, and the second is the one worth keeping. My runs were valid and my caveat was not. Every mutation script I used did And On your larger findingA deleted object listed in Your explanation for the lifetime is better than my framing was. I said the failure mode Not chasing why |
jdatcmd
left a comment
There was a problem hiding this comment.
Approving 91f4e187. The line is fixed, and you went past what I asked for in a way that is worth keeping.
I reproduced the new claim rather than reading it
Same experiment on my tree. My hashes differ from yours because the .so fingerprint is a function of the build directory — the relation is what transfers, and it holds exactly:
baseline, header at 103 c67dd162c44cd86c
mutate to 2; rm -f obj; touch the .c; make compiled 2 files 9649379be700b359
mutate to 2; make clean; make 9649379be700b359
Byte-identical, and rm alone still compiles nothing. So rm -f obj; touch the .c is a measured equivalent to a clean build, and your table is right in both directions.
That is a better answer than the one I gave. I established that rm alone fails and stopped at "use make clean"; you established what the shortcut actually costs and why, which is the difference between a rule and a reason. Your closing clause is the part I would keep — make clean is what to reach for because it does not depend on knowing which object the header reached. That is the real hazard with the shortcut: it is correct only if you correctly guessed the reachable set, and a header with two consumers silently gives you a half-rebuilt library.
The sentence that earns the docstring
rm -f src/columnar_unique.oIS NOT ENOUGH, AND THIS COMMENT SAID IT WAS.
Leaving the wrong advice named in the text that replaces it is right. A reader who saw the old version elsewhere — in a review, in a branch, in their own notes — needs to know it was wrong, and a silent correction does not tell them.
The rest, re-checked at this head
two arms, not one confirmed: `reachable [(16520, 77, 2)]` and `BLOCKED by the user lock`
shell suite green confirmed: 7 passed + 0 failed + 0 unrunnable, PASSED
"discovery FEEDS the correct, and the right word -- one wrong premise disables
contention arm" both, both return when it is fixed
CI at 91f4e187 14 of 14, non-green 0, CLEAN
Still agreed on skipping the five-major gate for a docstring, for the reason you gave.
One thing for #1158 rather than this PR: your touch result narrows what the fix has to do. -MMD -MP gives correct header dependencies, and the touch case shows make does act on a .c timestamp — so the remaining gap is specifically that a missing object listed in OBJS does not itself trigger a rebuild. That is a smaller and more precise defect than "incremental builds do not work", which is how I had been describing it. I will correct that on the issue.
🤖 Generated with Claude Code
Closes #1157. My docstring claimed less than the file does, and the claim was reasoned
rather than run.
I wrote that the shell suite's discovery makes its contention arm "inherit the error".
@jdatcmd ran it instead of reading it, and found the port reddens two arms, not one.
I verified that here before writing this, on
f12caa2with the object forced to rebuild:The accurate statement is that the discovery feeds the contention arm: one wrong
premise disables both, and both come back when the premise is fixed. That is worse than
one vacuous arm rather than the same thing, because two arms move with the defect and both
keep looking like evidence.
And the rebuild caveat, which is the reason the proof is reproducible at all (#1158)
PGXS emits no header dependencies unless PostgreSQL was configured with
--enable-depend,and it was not here — so changing this
#definerebuilds nothing, the run measures astale object, and it reports a clean pass that reads exactly like "not load-bearing". The
.sohash not moving is the only tell. That is now in the docstring beside themeasurement, because the next person to re-run this proof will hit it otherwise.
Scope
Comment only. No assertion, no check name, no count changes —
git show --statis onefile and the docs-coverage guard confirms nothing moved.
What I ran, and what I did not
I did not run the five-major gate for this one. The change is a docstring; it cannot
fail differently on PG15 than on PG19, and a twelve-minute matrix would buy a number
rather than a fact. Saying so rather than quoting five greens I did not need is the point
— the earlier ports in this file got the full gate because they could differ by major.
🤖 Generated with Claude Code
https://claude.ai/code/session_012RSw4qMHS7ByE7PY8Ns4cs