fix(archiver): serve log-by-tag queries from read-only snapshots - #25315
Open
spalladino wants to merge 1 commit into
Open
fix(archiver): serve log-by-tag queries from read-only snapshots#25315spalladino wants to merge 1 commit into
spalladino wants to merge 1 commit into
Conversation
This was referenced Aug 25, 2026
spalladino
force-pushed
the
spl/log-store-read-only-snapshots
branch
from
August 25, 2026 21:13
b7948a5 to
ddb0e53
Compare
The log store's read paths now run inside `db.readOnlyTransaction` instead of `db.transactionAsync`. They still see one consistent snapshot for the `referenceBlock` reorg check and every per-tag scan, but no longer go through the store's single serial writer queue, so a query does not wait for queued writes (notably block ingestion) to commit. Only the read paths move; `addLogs` and `deleteLogs` stay on `transactionAsync`. Cursors bound to one snapshot serialize on that snapshot's mutex natively, so `TAG_SCAN_CONCURRENCY` drops from 8 to 2: on a seeded store a 100-tag query averages 7.4ms at 1, 4.9ms at 2, and 5.0ms at 4 and 8, and with five queries in flight the wider pools are marginally slower. Each open snapshot holds one LMDB reader slot, so the reader table size is now configurable via `DATA_STORE_MAX_READERS` / `DataStoreConfig.dataStoreMaxReaders` instead of being hardcoded, keeping the previous 16 as the default.
spalladino
force-pushed
the
spl/log-store-read-only-snapshots
branch
from
August 25, 2026 21:19
ddb0e53 to
fa7156a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stack
Part of A-1817. Bottom to top, each PR targets the branch below it:
spl/faster-block-ingestion->merge-train/spartan-v5— cheaper checkpoint ingestion, so writes hold the store for less timespl/kv-store-read-only-tx->spl/faster-block-ingestion— addsreadOnlyTransactionplustxIdon the nativeGET/START_CURSORspl/batched-kv-gets->spl/kv-store-read-only-tx— addsgetMany/getManyAsync; depends on feat(kv-store): expose read-only lmdb transactions #25280 fortxIdonGetRequestspl/faster-get-private-logs-by-tags->spl/batched-kv-gets— faster tag scans; depends on fix(archiver): batch multi-key point reads into one LMDB round trip #25282 forgetManyAsyncon the log store's per-block readsspl/log-store-read-only-snapshots->spl/faster-get-private-logs-by-tags— moves the log store's reads onto snapshots; depends on feat(kv-store): expose read-only lmdb transactions #25280 forreadOnlyTransaction<- this PRContext
Fixes A-1817.
#25254 kept tag log queries inside
db.transactionAsyncto preserve snapshot consistency, which on lmdb-v2 means routing them through the store's single serial writer queue: a query waits for every queued write (notably checkpoint ingestion) to commit before it even starts. #25280 addedreadOnlyTransaction, which gives the same one-snapshot guarantee without touching the writer queue, so the log store can have consistency and not queue.Approach
LogStore's read paths (getPrivateLogsByTags,getPublicLogsByTags,getPrivateLogsForBlock,getPublicLogsForBlock) switch fromdb.transactionAsynctodb.readOnlyTransaction. The inner reads already resolve the ambient transaction throughacquireReadTx, so these are one-line swaps.addLogsanddeleteLogsare untouched.The class JSDoc no longer justifies staying on the writer queue; it documents the snapshot instead.
TAG_SCAN_CONCURRENCYdrops from 8 to 2. Cursors bound to one read-only snapshot serialize on that snapshot's mutex in C++, so a wide pool only hides the per-cursor round trip. Measured on a seeded store (20 blocks x 4 txs x 5 private logs, 100-tag query, 30 iterations after warmup, two runs):TAG_SCAN_CONCURRENCYGoing from 1 to 2 buys a third of the latency; everything above 2 is flat uncontended and marginally worse under concurrency, while holding more cursors.
Each open snapshot holds one of the LMDB reader slots, so
MAX_READERSis no longer hardcoded inkv-store/src/lmdb-v2/factory.ts: it comes fromDataStoreConfig.dataStoreMaxReaders(DATA_STORE_MAX_READERS), defaulting to the previous 16.Testing
log_store_write_contentionnow asserts the read does not wait for the queued writes. With 5 x 50ms writes queued in front of it:transactionAsync(before): baseline 10.56ms, contended 259.92ms — failsreadOnlyTransaction(after): baseline 11.40ms, contended 8.39ms — passesThe bound is
contendedMs < baselineMs + queuedWriteMs / 2, leaving 125ms of jitter headroom so it only trips when the read is genuinely serialized.