refactor(cluster): read status from snapshot in scheduler - #3563
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR refactors scheduler-side status reads to reduce repeated SQLite queries while holding the global cluster lock by introducing an in-memory StatusSnapshot in status_db. The snapshot loads all relevant status tables with one query each and serves subsequent reads from in-memory lists, while still ensuring freshness via a per-process write generation counter and an explicit refresh after lock acquisition.
Changes:
- Add
status_db.StatusSnapshotplus a module-level_write_generationcounter to support auto-refresh on local writes and explicit refresh for cross-process writes. - Switch
cluster_getter.get_cluster_instancescheduler read paths from directstatus_db.*queries toself.snap.*snapshot accessors (refreshing once per lock acquisition). - Add
framework_testscoverage to validate snapshot parity with module-level read functions and refresh behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| framework_tests/test_status_db.py | Adds tests asserting StatusSnapshot accessor parity and refresh behavior. |
| cardano_node_tests/cluster_management/status_db.py | Introduces write generation tracking, shared filter matching, and the StatusSnapshot implementation. |
| cardano_node_tests/cluster_management/cluster_getter.py | Updates scheduler reads to use the snapshot and refreshes it immediately after acquiring the global lock. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The scheduling loop in `get_cluster_instance` queried the status database repeatedly - several queries per cluster instance in every iteration, all while holding the global cluster lock. Add `StatusSnapshot` to `status_db` that loads all status records with one query per table. Its read accessors mirror the module-level query functions, including the filter argument semantics, so the scheduler call sites change only from `status_db.*` to `self.snap.*`. Reads from the snapshot are plain list traversals, which shortens the time the global lock is held and makes scheduling decisions testable against an in-memory state. Writes keep going directly to the database. Every write done by this process bumps a module-level generation counter and the snapshot refreshes itself when its generation is stale, so mid-iteration writes (e.g. mark cleanup) stay visible to subsequent reads, same as with the direct queries. Writes done by other processes cannot happen while the lock is held - the scheduler refreshes the snapshot explicitly right after acquiring the lock. The `_respin` method intentionally keeps the direct database queries, as it runs outside the global lock.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The scheduling loop in
get_cluster_instancequeried the status database repeatedly - several queries per cluster instance in every iteration, all while holding the global cluster lock.Add
StatusSnapshottostatus_dbthat loads all status records with one query per table. Its read accessors mirror the module-level query functions, including the filter argument semantics, so the scheduler call sites change only fromstatus_db.*toself.snap.*. Reads from the snapshot are plain list traversals, which shortens the time the global lock is held and makes scheduling decisions testable against an in-memory state.Writes keep going directly to the database. Every write done by this process bumps a module-level generation counter and the snapshot refreshes itself when its generation is stale, so mid-iteration writes (e.g. mark cleanup) stay visible to subsequent reads, same as with the direct queries. Writes done by other processes cannot happen while the lock is held - the scheduler refreshes the snapshot explicitly right after acquiring the lock.
The
_respinmethod intentionally keeps the direct database queries, as it runs outside the global lock.