Skip to content

feat: optimize staking EndBlock/BeginBlock queue iterators#1768

Draft
thomas-nguy wants to merge 3 commits intocrypto-org-chain:release/v0.53.xfrom
thomas-nguy:thomas/optimise-staking-module
Draft

feat: optimize staking EndBlock/BeginBlock queue iterators#1768
thomas-nguy wants to merge 3 commits intocrypto-org-chain:release/v0.53.xfrom
thomas-nguy:thomas/optimise-staking-module

Conversation

@thomas-nguy
Copy link
Collaborator

Summary

Reduces staking EndBlock (and BeginBlock historical info) cost when chain and queue data grow by avoiding full-prefix scans on time/height-ordered queues. Iterators now start from a tracked queue head (or, for HistoricalInfo, a min height) and can early-exit when there are no mature entries.

Problem

Profiling showed bottlenecks in:

  • ValidatorQueueIterator
  • UBDQueueIterator
  • RedelegationQueueIterator

Each was iterating from the prefix start (e.g. time 0) up to the current block time/height every block. As the chain and queues grew, that range and the cost of creating/using the iterator increased even when only a few entries matured per block.

Solution

  1. Queue head tracking (Validator, UBD, Redelegation)
  • Persist the minimum (earliest) completion key still in each queue.

  • Iteration: Start the iterator from this head key instead of the prefix start (fallback to prefix start if head is missing).

  • Early exit: If the stored head is after current block time/height, skip creating the iterator and return.

  • Head updates:
    On enqueue (e.g. SetUBDQueueTimeSlice, SetRedelegationQueueTimeSlice, SetUnbondingValidatorsQueue): set head when the new key is earlier than the current head (or head is unset).

    On dequeue (UBD and Redelegation only): after removing mature timeslices, set head to the next remaining key, or clear head if the queue is empty. 
    
  • Validator queue: head is updated only on enqueue; if the head key is later deleted, the next block’s iterator still starts at that key and the store seeks to the next existing key.

  1. HistoricalInfo (BeginBlock)
  • Persist the minimum stored height for historical info.
  • IterateHistoricalInfo: Start from GetHistoricalInfoKey(minHeight) when the min-height key is set.
  • TrackHistoricalInfo: After pruning, set the min-height key to blockHeight - entryNum + 1.
  • SetHistoricalInfo: When the min-height key is missing (e.g. genesis), set it to the current height so iteration is bounded from the first use.

Author Checklist

All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.

I have...

  • included the correct type prefix in the PR title
  • added ! to the type prefix if API or client breaking change
  • targeted the correct branch (see PR Targeting)
  • provided a link to the relevant issue or specification
  • followed the guidelines for building modules
  • included the necessary unit and integration tests
  • added a changelog entry to CHANGELOG.md
  • included comments for documenting Go code
  • updated the relevant documentation or specification
  • reviewed "Files changed" and left comments if necessary
  • run make lint and make test
  • confirmed all CI checks have passed

Reviewers Checklist

All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.

I have...

  • confirmed the correct type prefix in the PR title
  • confirmed ! in the type prefix if API or client breaking change
  • confirmed all author checklist items have been addressed
  • reviewed state machine logic
  • reviewed API design and naming
  • reviewed documentation is accurate
  • reviewed tests and test coverage
  • manually tested (if applicable)

@randy-cro
Copy link

randy-cro commented Feb 19, 2026

I remember I did something similar to this, ie trying to reduce the range of iteration, before trying to create an in memory cache. However, the duration taken to create the iterator still causes significant lag in the endblocker, staking endblocker is still taking around 700ms


// SetHistoricalInfo sets the historical info at a given height
// SetHistoricalInfo sets the historical info at a given height. When the min-height
// hint is unset (e.g. genesis), sets it to this height so IterateHistoricalInfo is bounded.

Choose a reason for hiding this comment

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

dont think historical info is exported or imported for genesis

@thomas-nguy thomas-nguy marked this pull request as draft February 19, 2026 09:05
@thomas-nguy thomas-nguy force-pushed the thomas/optimise-staking-module branch 4 times, most recently from 2dbf89d to 6a40c78 Compare February 19, 2026 09:26
@github-actions github-actions bot removed the C:x/gov label Feb 19, 2026
@thomas-nguy thomas-nguy force-pushed the thomas/optimise-staking-module branch from 6a40c78 to b9ce84f Compare February 19, 2026 09:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments