Skip to content

experiment: remove bloom filter from header#3854

Open
EgeCaner wants to merge 5 commits into
mainfrom
exp/perf/remove-bloom-filter-from-header
Open

experiment: remove bloom filter from header#3854
EgeCaner wants to merge 5 commits into
mainfrom
exp/perf/remove-bloom-filter-from-header

Conversation

@EgeCaner

Copy link
Copy Markdown
Contributor

No description provided.

@EgeCaner EgeCaner added the disable-deploy-test We don't want to run deploy tests with this PR because it might affect our development environment. label Jul 23, 2026
@EgeCaner
EgeCaner force-pushed the exp/perf/remove-bloom-filter-from-header branch from 6cfbdcd to e385bc7 Compare July 23, 2026 11:00
@codecov

codecov Bot commented Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.18519% with 36 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.55%. Comparing base (b5235d0) to head (aea2729).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
migration/blockeventsbloom/blockeventsbloom.go 83.33% 5 Missing and 4 partials ⚠️
migration/blockeventsbloom/ingestor.go 75.00% 4 Missing and 3 partials ⚠️
migration/deprecated/migration.go 37.50% 3 Missing and 2 partials ⚠️
sync/sync.go 80.00% 3 Missing ⚠️
core/running_event_filter.go 33.33% 1 Missing and 1 partial ⚠️
migration/blockeventsbloom/committer.go 87.50% 1 Missing and 1 partial ⚠️
migration/deprecated/deprecated_block_header.go 81.81% 1 Missing and 1 partial ⚠️
pruner/running_event_filter.go 33.33% 1 Missing and 1 partial ⚠️
node/migration.go 0.00% 1 Missing ⚠️
p2p/sync/sync.go 0.00% 1 Missing ⚠️
... and 2 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3854      +/-   ##
==========================================
- Coverage   76.45%   75.55%   -0.91%     
==========================================
  Files         442      446       +4     
  Lines       39767    39856      +89     
==========================================
- Hits        30405    30114     -291     
- Misses       7221     7653     +432     
+ Partials     2141     2089      -52     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@EgeCaner
EgeCaner force-pushed the exp/perf/remove-bloom-filter-from-header branch from b7d9bae to e070c39 Compare July 23, 2026 16:10
@EgeCaner
EgeCaner force-pushed the exp/perf/remove-bloom-filter-from-header branch from e070c39 to aea2729 Compare July 24, 2026 14:24
@EgeCaner
EgeCaner marked this pull request as ready for review July 24, 2026 14:24
@EgeCaner
EgeCaner requested review from brbrr, infrmtcs and rodrodros July 24, 2026 14:25
@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Claude finished @EgeCaner's task in 6m 53s —— View job


PR Review: remove bloom filter from header

I reviewed the full diff against origin/main. The refactor is clean and consistent: EventsBloom is dropped from core.Header, computed on demand from receipts (core.EventsBloom), threaded through Store/Finalise/state backends, and carried alongside blocks/pre-confirmed via the new core.WithBloom[T] on the sync/sequencer/pruner/RPC feeds. The DeprecatedBlockHeader shim + blockeventsbloom migration to strip the on-disk bloom is well thought out (idempotent, resumable, respects the retention floor, correct field/cbor-tag preservation). All library packages compile (cmd/juno only fails at the final Rust-cgo link, an environment issue, not a code one).

Three things worth addressing, posted inline:

Important

  • node/migration.go:31 — the optional headstate.Migrator{} (--new-state) registration and its import were removed and replaced by blockeventsbloom.Migrator, while cfg.NewState is still wired everywhere else. Looks like a rebase artifact; --new-state nodes would silently skip that migration. Please confirm intent.
  • blockchain/event_filter.go:347 — the skippedEvents = 0 reset on the bloom-miss path in preConfirmedEvents was dropped. On a continuation resume where a volatile pre-confirmed block's content changed and its bloom no longer matches, skippedEvents leaks into the next matching block and silently drops its first N events. The original code had an explicit comment guarding exactly this.

Nit / perf

  • sync/preconfirmed/chain_storage.go:570 — delta updates now recompute the full block bloom over all receipts every poll (via NewPreConfirmedWithBloom) instead of the previous incremental merge of only the appended receipts. ~O(N²) bloom work over a growing pre-confirmed block's lifetime on the poller hot path. Fine for an experiment, but worth keeping the incremental merge if it shows up in profiles.

Everything else (statebackend plumbing, genesis, builder/sequencer, adapters, p2p sync, RPC v8/v9/v10 subscriptions, walkOldestFirstWithBloom pointer-into-immutable-node, nil-guard on poller.apply) looks correct.

Comment thread node/migration.go
PruneModeFlag,
).
WithOptional(&headstate.Migrator{}, cfg.NewState, "new-state")
With(&blockeventsbloom.Migrator{})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important — looks like an accidental drop during the rebase. This PR removes the previously-registered optional headstate.Migrator{} (WithOptional(&headstate.Migrator{}, cfg.NewState, "new-state")) and its import, replacing it with the new blockeventsbloom.Migrator. But cfg.NewState / new-state is still wired up everywhere else (node/node.go:149,240,264,785, node/genesis.go), and the migration/state/headstate package still exists and is otherwise unregistered anywhere in the tree.

The net effect: nodes started with --new-state will no longer run the head-state migration. Since this is unrelated to removing the bloom filter (and the last commit is "rebase fix"), please confirm whether the intent was to also register blockeventsbloom alongside the headstate migrator rather than in place of it:

	registry := migration.NewRegistry().
		With(&blocktransactions.Migrator{}).
		WithOptional(
			historyprunner.New(cfg.RetainedBlocks, cfg.PruneMinAge),
			cfg.Prune,
			PruneModeFlag,
		).
		WithOptional(&headstate.Migrator{}, cfg.NewState, "new-state").
		With(&blockeventsbloom.Migrator{})

Fix this →

Comment on lines +347 to 349
if !e.matcher.TestBloom(entryWithBloom.Bloom) {
continue
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important — behavioral regression: the skippedEvents = 0 reset in the bloom-skip branch was dropped. On origin/main this branch read:

if !e.matcher.TestBloom(header.EventsBloom) {
    // Skipped events are scoped to the resume block; once we step past
    // it, reset so later blocks aren't under-counted.
    skippedEvents = 0
    continue
}

Now the reset is gone, so skippedEvents (seeded from cToken.processedEvents, which is scoped to the resume block only) survives a bloom miss. Scenario: a continuation token resumes at fromBlock=X with processedEvents=5. Pre-confirmed blocks are volatile — a delta/replace-slot can change block X's content between requests. If X's new bloom no longer matches, we continue without resetting, and the next matching block X+1 then gets AppendBlockEvents(..., skippedEvents=5, ...), silently dropping its first 5 events.

Restore the reset in the skip path:

Suggested change
if !e.matcher.TestBloom(entryWithBloom.Bloom) {
continue
}
if !e.matcher.TestBloom(entryWithBloom.Bloom) {
// Skipped events are scoped to the resume block; once we step past
// it, reset so later blocks aren't under-counted.
skippedEvents = 0
continue
}

return nil, nil, err
}
newNode := &node{preconfirmed: &next, parent: target.parent}
newNode := &node{entry: pending.NewPreConfirmedWithBloom(&next), parent: target.parent}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance — delta updates now recompute the full block bloom instead of an incremental merge. NewPreConfirmedWithBloom(&next) calls core.EventsBloom(next.Block.Receipts) over all receipts. Previously (adapters/sn2core/sn2core.go AdaptPreConfirmedWithDelta, now removed) only the newly-appended receipts were bloomed and merged into the existing filter:

addedBloom := core.EventsBloom(mergedReceipts[len(existingTxs):])
addedBloom.Merge(current.Block.Header.EventsBloom)

Delta updates arrive on every poll while a pre-confirmed block grows, so a block that ends with N events now costs O(N) bloom work per delta → ~O(N²) over the block's lifetime, on the poller hot path. Worth confirming this is acceptable for this experiment, or keeping the incremental merge (e.g. threading the parent's bloom through the delta path) if it shows up in profiles.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

disable-deploy-test We don't want to run deploy tests with this PR because it might affect our development environment.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant