Skip to content

chore: Incremental account codes pruning - #2463

Open
sergerad wants to merge 1 commit into
nextfrom
sergerad-account-codes-prune-perf
Open

chore: Incremental account codes pruning#2463
sergerad wants to merge 1 commit into
nextfrom
sergerad-account-codes-prune-perf

Conversation

@sergerad

@sergerad sergerad commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Closes #2444.

The vault and storage prunes are incremental: each block only visits rows that expired since the previous prune. The account-code prune instead re-scanned every account row still valid past the cutoff on every block.

This PR makes the codes prune churn-driven. A code pinned at one prune can only become collectable at a later one if its last referencing row expired between the two cutoffs, so the prune scans just that window for candidates and probes each for a surviving reference — O(churn) per block, like the other two prunes.

Implementation notes:

  • Migration 005 adds idx_accounts_code_probe (backs the existence probe) and a single-row prune_progress table recording the cutoff through which code pruning has completed. The marker is updated in the same transaction as the prune, so it is crash-consistent and survives restarts.
  • With no marker recorded (first prune after migration, or a fresh database), the previous full-pass query runs once.
  • When the cutoff hasn't advanced (e.g. stalled prune_tip), the prune now skips entirely instead of re-scanning the live set.

Changelog

changelog = "none"
reason    = "Internal change only."

@kkovaacs kkovaacs left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good to me!

@Mirko-von-Leipzig

Copy link
Copy Markdown
Collaborator

This is perhaps a dumb question; but why do we even need to prune code?

I imagine code changes are rare and even then code would largely be shared amongst most accounts?

Comment on lines +1290 to +1291
// Seed [2u8; 32] keeps the account ID distinct from the other test helpers.
build_account_with_code_seeded(push_value, [2u8; 32])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Okay, but why?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Please remove the test_ prefix

Comment on lines +1664 to +1667
/// Prune test 6: `prune_progress` records the cutoff of the last codes prune; re-pruning at the
/// same or a lower cutoff deletes nothing and never moves the marker backwards.
#[test]
fn test_prune_account_codes_marker_never_regresses() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't quite understand this check tbh. When or how would this happen; and if it does, why does it matter?

Comment on lines +1739 to +1753
/// The prune is churn-driven: a code pinned at the previous prune (some row with
/// `valid_until > prev_cutoff` referenced it) can only become collectable now if the row holding
/// its maximal `valid_until` expired inside `(prev_cutoff, cutoff_block]`. Candidate codes are
/// therefore collected from that window — an `idx_accounts_code_validity` range scan sized by
/// churn since the previous prune — and each is deleted only if the `idx_accounts_code_probe`
/// existence probe finds no row still referencing it past the cutoff. The previous cutoff is
/// persisted in `prune_progress` within the same transaction; when absent (first prune after
/// migration, or a fresh database) a full pass over all rows valid past the cutoff runs instead.
///
/// Correctness of the windowed candidate set rests on two invariants:
/// - Rows are only ever closed to the `block_num` of the block currently being applied, which is
/// always above the cutoff, so every expiry crosses the window of some later prune. A write path
/// that back-dated `valid_until` below the current cutoff would leak the code forever.
/// - Every `account_codes` row is inserted alongside an `accounts` row referencing it (see
/// [`upsert_accounts`]); an orphan code with no referencing row would never become a candidate.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think I've re-read this 5x now - I don't understand what this is saying. Is there a simpler way to explain this?

This is the first time I've encountered churn driven.

Comment on lines +1774 to +1776
// Codes are already pruned through this cutoff and nothing can become collectable while the
// cutoff stands still, so skip without moving the marker backwards.
Some(prev_cutoff) if prev_cutoff >= cutoff_block => return Ok(0),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

When would this happen? And if its caller error, should this value not be determined from within this function?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I guess I don't understand why we aren't just pruning one block at a time as the chain moves?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve account code pruning performance

3 participants