etl: enable Append/AppendDup for certain state tables during etl loading#19956
etl: enable Append/AppendDup for certain state tables during etl loading#19956sudeepdino008 wants to merge 5 commits intomainfrom
Conversation
Sort ETL entries by (key, value) at flush time for ii/history Keys tables, enabling AppendDup where keys (txNums) are always ascending. This replaces Put with AppendDup for Keys tables, achieving 100% append ratio. Changes: - Add SortByKeyAndValue() to sortableBuffer for (key, value, insertionOrder) sort - Add sortValues flag to Collector with chainable SortValues(bool) setter - Pass sortValues through FlushToDisk/FlushToDiskAsync/sortAndFlush - Add sortValues field to Heap for DupSort-aware merge (gated behind haveSortingGuaranties && isDupSort && sortValues) - indexKeys collectors: .SortValues(true) + IdentityLoadFunc (AppendDup) - index collectors: loadFunc (Put, unchanged) - DupSort history vals: loadFunc (Put, unchanged) - Non-DupSort history vals: IdentityLoadFunc (enables Append) - Domain vals: unchanged (custom loadFunc preserved) - Deduplicate consecutive identical (key, value) pairs in AppendDup path to handle parallel executor retries - Add per-Load metrics: put/append/appendDup/dupSkip counts with appendRatio Validated on Sepolia stage_exec: 0 gas mismatches, 0 MDBX errors, Keys tables at 100% AppendDup across 21 commit cycles. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…story annotation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
│ Metric │ main │ Feature │
TODO:
|
|
look at new etl sort in But there is one more trick which i decided to not release yet: cast first 8 bytes prefix of value to u64 and use Only 1 problem: high chance that values are already sorted? |
|
also now we have stepSize/4 - maybe just enable |
|
things i learned:
|
Summary
Enable
AppendDupfor DupSort Keys tables (ii/history keys) by sorting ETL entries by(key, value)at flush time. Keys tables use txNum as the key, and since txNums are always increasing during execution, new data is always past the end of the table — makingAppend/AppendDupsafe. With value sorting,AppendDupworks correctly for these DupSort tables.AccountIdxkeyed by address,StorageHistoryValskeyed by address+slot,LogTopicsIdxkeyed by topic hash), the first ETL key is almost always ≤ the last key in the table, socanUseAppendis false for the entire load and everything falls back toPut.canUseAppendapproach (switching between Append and Put mid-load based on key comparison) was tried but did not yield better results — when keys are randomly distributed, the vast majority of entries require Put anyway, and the overhead of per-entry switching negates any benefit from the rare Append.Changes
(key, value)at flush time for Keys tables, enablingAppendDup— all records use AppendDup instead of PutIdentityLoadFuncmigration: onlyindexKeyscollectors and non-DupSort history vals use it; everything else keepsloadFunc(forces Put) to preserve insertion-order-dependent upsert logic (domain non-largeVals)(key, value)pairs in the AppendDup path — retried transactions can produce duplicate entries that Put handles idempotently but AppendDup rejects withMDBX_EKEYMISMATCHPer-table behavior
canUseAppendis true (1st commit only in practice — key includes address prefix → scattered after)canUseAppendis true (1st commit only in practice — key=address+txNum→ scattered after)Safety constraint
The heap's DupSort value comparison is gated behind
haveSortingGuaranties && isDupSort && c.sortValues— never applied to domain non-largeVals flush which uses a custom loadFunc with SeekBothRange/DeleteCurrent/Put that depends on insertion order.Future: Receipt history vals
ReceiptHistoryValsis DupSort (HistoryLargeValues: false) and currently usesloadFunc(all Put). However, Receipt entity keys may be sequential enough forcanUseAppendto hold, meaning AppendDup could also work here. This would require addingIdentityLoadFuncand potentially.SortValues(true)for the Receipt history vals collector. Left as a follow-up to validate separately.Sepolia validation
Ran
stage_exec --batchSize=10mbfor 10 minutes on identical Sepolia state (2 step ranges removed):Test plan
go test ./db/etl/ -count=1— all pass (includes 6 new tests + 1 benchmark)go test ./db/state/ -count=1 -short— all passmake erigon integration— builds cleanmake lint— 0 issues (2 runs)stage_exec— 0 gas mismatches, 0 errors across 21 commit cycles🤖 Generated with Claude Code