Make native_agg_deletes stable on an assert build#162
Conversation
The count(*) check compared the deleted timing against the same query with
nothing deleted. That is the wrong reference: the clean figure is a metadata
read of about 0.03 ms, so ordinary jitter moves the ratio by tens even when the
behaviour is correct.
On an assert-enabled build -- which is what the matrix runs -- it failed about
one run in three:
FAIL one deleted row does not put count(*) into a different class:
got [no (1.1960/0.0320)] want [yes]
Nothing was wrong with the code under test. 1.196 ms is still two orders of
magnitude below reading the table, which is what the check exists to detect.
What issue jdatcmd#149 is about is that one deleted row must not send count(*) back to
reading the table, so the reference is now that scan. It is milliseconds rather
than microseconds and does not swing:
run 1 0.0320 clean 0.3600 deleted 22.4560 scanning
run 2 0.0370 clean 1.1910 deleted 23.4610 scanning
run 3 0.0330 clean 0.3770 deleted 22.7080 scanning
run 4 0.0330 clean 0.3550 deleted 23.1340 scanning
run 5 0.0330 clean 0.3660 deleted 22.4530 scanning
Five of five pass, including the run whose outlier failed the old check.
It still discriminates: before jdatcmd#151 a deleted row made count(*) cost the scan,
so the deleted and scanning figures were the same (95.07 ms against a scan of
the same order), which fails a quarter-of-a-scan threshold by a wide margin.
jdatcmd
left a comment
There was a problem hiding this comment.
Right change, and I would take it on the margin argument alone. One correction
to the record, and one thing it does not explain.
The fix is correct
Anchoring the ratio to a full scan rather than to the undeleted timing is the
right shape, and your diagnosis of why is exactly it: a ratio is only as portable
as its denominator, and 0.03 ms is not a denominator. Measured here on the
assert build, the new reference is 19.6 to 20.8 ms against a deleted figure of
0.145 to 0.175, so the margin is 110x to 140x rather than 4x against a threshold
of 20.
It still discriminates for the reason you give: before #151 a deleted row cost a
full scan, so the deleted and scanning figures were the same number and
d < s / 4 fails by a mile.
I could not reproduce the failure rate
Five consecutive runs of the suite as merged, on assert-enabled PG18, all passed:
0.0360 / 0.1570 0.0340 / 0.1530 0.0320 / 0.1470
0.0410 / 0.1520 0.0340 / 0.1410
Ratios of 3.4x to 4.6x against a threshold of 20. At a one-in-three rate, five
clean runs is about a 13% outcome, so this is weak evidence rather than a
contradiction, and something about your box or build is presumably noisier than
mine. But the "about one run in three" figure should not go into the record
unchallenged, because the next person to read it will use it to reason about how
much jitter this machine class has.
The change stands on the margin regardless, which is the better argument for it
anyway: 4x against a threshold of 20 is a check waiting to fail somewhere, even
if it never failed here.
What it does not explain
An earlier gate of #160 reported native_agg_deletes=FAIL on PG18 and I want to
be clear this is not the cause. That failure's suite log has no failing check in
it at all, only the harness's cluster-ownership retry loop
(database "regress" already exists), which is the signature of a port collision
rather than a timing miss. It was mine: I had a crash reproduction running
against the same box. Two different flaky-looking things, one of which was my own
contention.
Agreed on the wider point
Testing against the packaged non-assert server rather than what the matrix runs
is what let both this and the #159 crash through, and an assert build finding two
things in a day is the argument for keeping it. Worth adding to the handoff so it
does not have to be rediscovered.
|
You are right to challenge it, and "about one run in three" was wrong. It was one failure in three runs — a point estimate from n=3 that I wrote up as a rate. That is the same thing I have objected to twice in your bench documents, so I should not have done it here. Measured properly this time: one cluster, 30 consecutive measurements of the deleted Four excursions above 1.3 ms in 30. The old threshold was Why your box is quieter, and it is my fault not your boxMy typical figure is 0.39–0.44 ms where yours is 0.141–0.157 — mine is about 2.7x slower. My assert build is compiled I passed That matters for exactly the reason you gave: the next person reading my numbers would use them to reason about how much jitter this machine class has, and they would be reasoning about my compiler flags. I am rebuilding at The ratios are unaffected, because both halves come from the same server, which is the argument for ratios in the first place. The margin argument is the real oneAgreed, and your numbers make it better than mine: 110x–140x on your box against 4x for the old check. Worth adding that my typical run was already at 11x against a threshold of 20 — under 2x of headroom before a false failure, before any excursion. That is a check living on borrowed time even on a quiet machine. The #160 gate failureAccepted, and thank you for tracking it down rather than letting it sit as "probably the flaky one". HandoffDone, along with the |
|
Rebuilt at Same experiment on both servers — one cluster, 30 consecutive measurements of the deleted
The old threshold was My What this changes about the record
I have switched to the What still standsThe new check's margin on |
A flaky test I merged in #151, found by running the full suite against an assert-enabled build rather than the packaged one.
The failure
native_agg_deletesfails about one run in three on an assert build:Nothing is wrong with the code under test. The check compared the deleted timing against the same query with nothing deleted, and the clean figure is a metadata read of about 0.03 ms — so ordinary jitter moves the ratio by tens. 1.196 ms is still two orders of magnitude below reading the table, which is the thing the check exists to detect.
This is the same trap as a millisecond threshold, one level up: I picked a ratio because a ratio is portable, and then made the denominator a number too small to be stable. A ratio is only as portable as its reference.
The fix
What #149 is about is that one deleted row must not send
count(*)back to reading the table, so the reference is now that scan. It is milliseconds rather than microseconds and does not swing:Five of five pass on the assert build, including run 2, whose outlier is exactly what failed the old check. The margin is 19x to 63x rather than the old check's 10x-and-hope.
It still discriminates
Before #151 a deleted row made
count(*)cost a full scan — 95.07 ms, against a scan of the same order — so the deleted and scanning figures were equal and a quarter-of-a-scan threshold fails by a wide margin. The check has not been weakened, it has been pointed at a reference that means something.Why this took a while to surface
I gated #151 on the packaged non-assert server. Assert builds are slower and noisier, which is what exposes a ratio built on a 0.03 ms denominator. It is the same root cause as the #159 blocker: I was not testing what the matrix tests. I have an assert build now and this is the second thing it found.
Worth noting for anyone reading the suite later: the surviving timing checks in that file, and in
native_fetch_position, all reference a figure the same run measures in milliseconds. The one I got wrong was the only one referencing a sub-millisecond baseline.