Context
The 2026-07-21 storage sweep found that admin list-view cold queries regressed ≈2× since the April baseline (page-list at 100k: 128 → 276 ms; filter+sort: 352 → 530 ms). Single-doc reads, batch fetch, and populate are unchanged — the regression is confined to the list path.
Root cause (confirmed in the 100k EXPLAIN plan): the document-grain / version split moved order_key and source_locale out of the version stream into byline_documents, so the byline_current_documents view now joins byline_documents to reunite the two grains. Because the outer list query does SELECT d.*, that join is evaluated across every current version before ORDER BY … LIMIT 20:
Nested Loop (rows=100000 loops=1)
-> Subquery Scan on sq (windowed current versions) ~99k buffers
-> Index Scan using byline_documents_pkey loops=100000 Buffers: shared hit=400000
400k of the query's 498k shared-buffer hits (~80%) are this per-current-version lookup. The getDocumentsByDocumentIds plan carries the same join but stays flat because it joins only 50 rows — proving the cost is specifically join-applied-pre-pagination-across-the-whole-collection, not the join existing.
Decision — deferred, not urgent
Accepted as-is for the scale Byline targets. A typical deployment is well under 10k documents, where page-list is ~32 ms and imperceptible; the public cache-miss paths (single-doc reconstruct, populate) are untouched. This is tracked, not scheduled.
Named trigger to pick this up: a real large-collection deployment (50k+ documents in one collection) reporting sluggish admin list pagination.
The experiment
The ORDER BY created_at DESC, id DESC keys both live on the version row (sq), not on byline_documents — so the sort + LIMIT 20 can complete entirely within the windowed version rows, joining byline_documents only to the ≤20 survivors. The view can't express this today because it projects order_key/source_locale through sq. Options to try in the findDocuments list-view path:
Sub-item: filter+sort has a second, independent bottleneck
The filter+sort query's plan shows a Parallel Seq Scan on byline_store_text for title ILIKE '%storage%' (~120 ms, ~134k rows removed per worker) — a leading-wildcard substring match, unindexable without trigrams. This is separate from the view-join regression and the deferred-join fix won't touch it.
References
Context
The 2026-07-21 storage sweep found that admin list-view cold queries regressed ≈2× since the April baseline (page-list at 100k: 128 → 276 ms; filter+sort: 352 → 530 ms). Single-doc reads, batch fetch, and populate are unchanged — the regression is confined to the list path.
Root cause (confirmed in the 100k
EXPLAINplan): the document-grain / version split movedorder_keyandsource_localeout of the version stream intobyline_documents, so thebyline_current_documentsview now joinsbyline_documentsto reunite the two grains. Because the outer list query doesSELECT d.*, that join is evaluated across every current version beforeORDER BY … LIMIT 20:400k of the query's 498k shared-buffer hits (~80%) are this per-current-version lookup. The
getDocumentsByDocumentIdsplan carries the same join but stays flat because it joins only 50 rows — proving the cost is specifically join-applied-pre-pagination-across-the-whole-collection, not the join existing.Decision — deferred, not urgent
Accepted as-is for the scale Byline targets. A typical deployment is well under 10k documents, where page-list is ~32 ms and imperceptible; the public cache-miss paths (single-doc reconstruct, populate) are untouched. This is tracked, not scheduled.
Named trigger to pick this up: a real large-collection deployment (50k+ documents in one collection) reporting sluggish admin list pagination.
The experiment
The
ORDER BY created_at DESC, id DESCkeys both live on the version row (sq), not onbyline_documents— so the sort +LIMIT 20can complete entirely within the windowed version rows, joiningbyline_documentsonly to the ≤20 survivors. The view can't express this today because it projectsorder_key/source_localethroughsq. Options to try in thefindDocumentslist-view path:ORDER BY+LIMIT) withoutbyline_documents, thenLEFT JOIN LATERAL byline_documentson the paginated set only.order_key/source_localeonly for the surviving rows (deferred projection) rather than through the view.Sub-item: filter+sort has a second, independent bottleneck
The filter+sort query's plan shows a
Parallel Seq Scan on byline_store_textfortitle ILIKE '%storage%'(~120 ms, ~134k rows removed per worker) — a leading-wildcard substring match, unindexable without trigrams. This is separate from the view-join regression and the deferred-join fix won't touch it.pg_trgmGIN index onbyline_store_text.valueto cover theILIKE '%…%'half. Measure the write-path and index-size cost against the read gain before committing.References
benchmarks/storage/results/2026-07-21-storage-cold-summary.md(findings fix: update README for starting projects. #2, fix(field): Fix some UI detail issues. #3)benchmarks/storage/results/2026-07-21-storage-cold-100k.mdbenchmarks/storage/results/2026-04-18-storage-cold-summary.mddocs/03-architecture/01-document-storage.md