(Requires Audit) PLEX-2473 LP Sparce Block Store (Part 3)#374
(Requires Audit) PLEX-2473 LP Sparce Block Store (Part 3)#374dhaidashenko merged 14 commits intodevelopfrom
Conversation
|
498dfff to
4cad2fc
Compare
0833d36 to
715d9ba
Compare
4cad2fc to
3fb2047
Compare
3fb2047 to
0c4981b
Compare
0c4981b to
8d8ff31
Compare
8d8ff31 to
c681f60
Compare
|
This PR is stale because it has been open 30 days with no activity. |
|
This PR has been automatically closed because it has been stale for > 30 days. |
The base branch was changed.
There was a problem hiding this comment.
Pull request overview
Adds support for a “sparse blocks” storage mode in the LogPoller to reduce DB writes by optionally skipping persistence of empty (no-log) blocks, and updates reorg/LCA logic plus configuration plumbing to support it.
Changes:
- Add
SelectNewestBlockto the LogPoller ORM and ObservedORM to support ancestor lookup in a sparse blocks table. - Introduce
Opts.SkipEmptyBlocks/LogPollerSkipEmptyBlocksconfiguration and wire it through chain config -> LogPoller. - Update LogPoller reorg handling/LCA search and expand tests for sparse-block scenarios (including skip-empty-blocks).
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/logpoller/orm.go | Adds SelectNewestBlock query for newest block at/below a given height. |
| pkg/logpoller/observability.go | Adds observed wrapper for SelectNewestBlock. |
| pkg/logpoller/log_poller.go | Adds SkipEmptyBlocks option and updates unfinalized log/block collection + LCA search for sparse DB blocks. |
| pkg/logpoller/log_poller_test.go | Refactors/extends tests for skip-empty-blocks and adds DB-vs-geth consistency helpers. |
| pkg/logpoller/log_poller_internal_test.go | Adds dedicated unit tests for findBlockAfterLCA under sparse DB conditions. |
| pkg/config/toml/docs.toml | Documents LogPollerSkipEmptyBlocks. |
| pkg/config/toml/defaults/fallback.toml | Provides default for LogPollerSkipEmptyBlocks. |
| pkg/config/toml/defaults.go | Ensures defaults copy includes LogPollerSkipEmptyBlocks. |
| pkg/config/toml/testdata/config-full.toml | Adds LogPollerSkipEmptyBlocks to full config fixture. |
| pkg/config/toml/config.go | Adds LogPollerSkipEmptyBlocks to TOML Chain struct. |
| pkg/config/toml/config_test.go | Updates config test expectations to include the new field. |
| pkg/config/config.go | Extends EVM config interface with LogPollerSkipEmptyBlocks(). |
| pkg/config/chain_scoped.go | Implements LogPollerSkipEmptyBlocks() accessor. |
| pkg/config/mocks/evm.go | Updates generated mock for new config method. |
| pkg/chains/legacyevm/chain.go | Wires config flag into logpoller.Opts.SkipEmptyBlocks. |
| CONFIG.md | Documents the new TOML setting. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -1262,8 +1270,11 @@ func (lp *logPoller) getUnfinalizedLogs(ctx context.Context, currentBlock *evmty | |||
| FinalizedBlockNumber: finalized, | |||
| SafeBlockNumber: safe, | |||
| } | |||
| logs = append(logs, convertLogs(rpcLogs, []Block{block}, lp.lggr, lp.ec.ConfiguredChainID())...) | |||
| blocks = append(blocks, block) | |||
| logs = append(logs, convertLogs(rpcLogs, []Block{*block}, lp.lggr, lp.ec.ConfiguredChainID())...) | |||
| // Skip empty blocks if configured to do so. | |||
| if len(rpcLogs) > 0 || !lp.skipEmptyBlocks { | |||
| blocks = append(blocks, *block) | |||
| } | |||
There was a problem hiding this comment.
getUnfinalizedLogs currently will not compile: it declares var block *Block and then shadows it with block := Block{...} inside the loop, but later dereferences *block and appends *block to slices. This also means the deferred checkpoint logic never sees the last block. Avoid shadowing and keep a single *Block (or use a differently named value like blk) and pass/append by value without dereferencing a non-pointer.
| func checkDBMatchesGeth(t *testing.T, orm logpoller.ORM, client logpoller.Client) bool { | ||
| // Check every block is identical | ||
| latest, err1 := client.HeadByNumber(testutils.Context(t), nil) | ||
| require.NoError(t, err1) | ||
| dbBlocks, err := orm.SelectLogsByBlockRange(t.Context(), 0, latest.Number) | ||
| require.NoError(t, err) | ||
| // ensure all blocks present in db are on geth canonical chain | ||
| for _, ourBlock := range dbBlocks { | ||
| gethBlock, err1 := client.HeadByNumber(testutils.Context(t), big.NewInt(ourBlock.BlockNumber)) | ||
| require.NoError(t, err1) | ||
| if ourBlock.BlockHash != gethBlock.Hash { | ||
| t.Logf("Initial poll our block differs at height %d got %x want %x\n", ourBlock.BlockNumber, ourBlock.BlockHash, gethBlock.Hash) | ||
| return false | ||
| } | ||
| } | ||
|
|
||
| latestDB, err := orm.SelectLatestBlock(t.Context()) | ||
| require.NoError(t, err) | ||
| require.Equal(t, latest.Number, latestDB.BlockNumber, "latest block number in db should match geth") | ||
|
|
||
| // ensure all logs present in db are on geth canonical chain | ||
| logs, err1 := orm.SelectLogsByBlockRange(t.Context(), 0, latest.Number) | ||
| require.NoError(t, err1) | ||
| for _, log := range logs { |
There was a problem hiding this comment.
checkDBMatchesGeth attempts to validate DB blocks against the canonical chain, but it calls orm.SelectLogsByBlockRange(...) and iterates those results as dbBlocks. This ends up checking log rows (and will miss blocks with no logs), and it duplicates the same logs query again later. Use a blocks query (e.g., orm.GetBlocksRange(...) or similar) for the blocks-table check, and keep the logs query only for validating log rows.
|
This reverts commit 938a963.
Required to reduce LP's DB usage.
Depends on #356