Repository navigation
Payload-dim upsert still writes on every sighting: ON CONFLICT DO UPDATE locks each presented row before its one-hour guard skips the UPDATE #4249
Copy link
Copy link
Closed
Labels
client-siteOwned by the client-site agents (other laptop). Local sessions never pick these up.Owned by the client-site agents (other laptop). Local sessions never pick these up.enhancementNew feature or requestNew feature or request
Description
Activity
- addedenhancementNew feature or requestNew feature or requestclient-siteOwned by the client-site agents (other laptop). Local sessions never pick these up.Owned by the client-site agents (other laptop). Local sessions never pick these up.
on Sep 25, 2026 Ruled: how this gets built
- Use the single-statement form from the body. A
NOT EXISTSprobe with a plain MVCC read skips rows seen within the hour, so only new and hour-stale digests reachON CONFLICT DO UPDATE. The Payload-dim batch upserts deadlock under concurrent collection cycles: unnest arrays carry no cross-session lock order #1801 lock order still covers every lock the statement takes. - The same change applies to every dimension that uses
PayloadDimensions.UpsertSql(query_plan_dim,query_text_dim, and any other). - Out of scope: probing digests first so payloads ship only for new ones. It saves network, not disk. The PR says so.
- Pins, on a live rig:
- A second upsert of the same batch within the hour leaves every row's
xmaxat 0 and movespg_current_wal_lsn()by less than a small bound. - A row stamped two hours ago is refreshed.
- A new digest is inserted.
- The Payload-dim batch upserts deadlock under concurrent collection cycles: unnest arrays carry no cross-session lock order #1801 concurrent reversed-batch test still passes.
- A second upsert of the same batch within the hour leaves every row's
- Lite: check whether Lite's dimension upsert writes on a re-sighting. DuckDB has no row locks or page images, so a change there is needed only if it rewrites rows. The PR says which.
🤖 Generated with Claude Code
- Use the single-statement form from the body. A
Addendum to the ruling above
The ruling said the #1801 lock order still covers every lock the statement takes. That is only true with one more change.
Today
PayloadDimensions.UpsertSqlhas noORDER BY. Its comment says the lock order comes from the order the client binds the digests, and does not depend on the plan. That holds only while the statement has no join. TheNOT EXISTSfilter adds one. A Hash Right Anti Join returns rows in hash order, not in the bound order.So the
SELECTthat feeds the insert getsORDER BYthe digest, asQueryStorePlanMap.UpsertSqldoes. Keep the client-side sort too. Update the comment to say why both are there. The #1801 test must run the new statement shape.🤖 Generated with Claude Code
- addedin-progressActively being worked by a local session or its agents (PR open or in flight)Actively being worked by a local session or its agents (PR open or in flight)
on Sep 25, 2026 - removedin-progressActively being worked by a local session or its agents (PR open or in flight)Actively being worked by a local session or its agents (PR open or in flight)
on Sep 25, 2026
Metadata
Metadata
Assignees
Labels
client-siteOwned by the client-site agents (other laptop). Local sessions never pick these up.Owned by the client-site agents (other laptop). Local sessions never pick these up.enhancementNew feature or requestNew feature or request
Related: #1801 (the lock-order requirement this keeps), #4130 (plan-dim purge)
Problem
PayloadDimensions.UpsertSqlis designed so that "the second and every later sighting of the same content writes nothing". Its conflict arm refresheslast_seenonly when the stored stamp is more than an hour old:The guard does stop the UPDATE. It doesn't stop the write. PostgreSQL locks every conflicting row before it evaluates that
WHERE. The INSERT reference says the rows the condition rejects are not updated, but "all rows will be locked when the ON CONFLICT DO UPDATE action is taken". So each re-sighted digest still produces:Heap/LOCKWAL record, and a dirtied heap page (the tuple'sxmaxchanges);initdb --data-checksums), an 8 KB full-page image the first time that page is touched in each checkpoint cycle;xmax, which is a second page image (FPI_FOR_HINT) if that reader gets to the page first in a new cycle.Every collection cycle presents the batch's plans and texts again, so the hot pages of
query_plan_dimandquery_text_dimare re-imaged roughly every checkpoint (5 min) and written back to disk just as often.Measured
Production SQL Server stores, build 3.8.0-nightly.20260924.467, 2026-09-25. Store A: 43 servers, ingests Query Store. Store B: 42 servers.
pg_stat_statementsdelta over a steady 55.6 min (04:45–05:41Z): thequery_plan_dimupsert ran 2,520 times. It wrote 671,162 WAL records, 109,861 of them page images, 0.85 GB of WAL: 23.2% of all WAL the store generated in the window (3.65 GB). It dirtied 125,471 buffers (1.03 GB).pg_stat_all_tablesshows 17,324 inserts and 10,605 updates onquery_plan_dim. Nearly all of the 671k records are tuple locks on rows the statement left unchanged.query_text_dimadded another 0.08 GB.pg_waldumpof the live WAL, per-record block references aggregated by relation (398 MB sample at 04:38Z): thequery_plan_dimheap was the largest single relation.Heap/LOCK: 39,085 records, 7,257 of them carrying page images, 45.6 MB = 12.0% of WAL;FPI_FOR_HINTon the same heap: 20.5 MB = 5.4%;FPI_FOR_HINTonquery_text_dim: another 2.2%.Heap/LOCKonquery_plan_dimat 19.9%.pg_waldump(1.1 GB sample at 05:41Z): thequery_plan_dimheap was again the largest relation, at 15.5% of WAL (FPI_FOR_HINT12.6%,Heap/LOCK1.6%,Heap/UPDATE1.2%), plus its pkey at 3.4%.xmax-invalid hints on tuples a previous cycle locked.Heap/LOCKwas the fourth-largest source of repeat images on store B: 3,335 of the cycle's 37,809.Where
Darling/PerformanceMonitor.Darling.Storage/PayloadDimensions.cs:336-355:UpsertSql, both arms (the gzip plan dim and the text dims). The doc comment at :301-326 describes the guard as writing nothing.Darling/PerformanceMonitor.Darling.Storage/PayloadDimensionWriter.cs:89: the call site, inside the batch's fact-COPY transaction.Fix shape
INSERT … ON CONFLICT DO NOTHINGfollowed by an orderedUPDATE … WHERE last_seen < $3 - '1 hour', which is two statements but the same effect.xmaxat 0, and itspg_current_wal_lsn()delta must be under a small bound;pg_stat_statements.wal_bytesfor the statement, andHeap/LOCKon the dim heaps inpg_waldump --stats.