Persist scoring window and voted swaps across validator restarts#17
Persist scoring window and voted swaps across validator restarts#17bitloi wants to merge 7 commits intoentrius:testfrom
Conversation
|
@LandynDev @anderdc Ready for review! |
|
Thanks, but this was solved and merged just before this was opened. If there's any necessary tweaks that should be made to the implementation, feel free to open another PR. Solution that was merged for this: #11 |
|
Thanks for the clarification. I re-checked the mapping and I think there’s a mismatch:
So while Would you please reopen and review this PR again? |
LandynDev
left a comment
There was a problem hiding this comment.
- Can we add tests? Can you also post results of unit tests passing after?
For example:
- round-trip save/load preserves all swap fields
- corrupted cache file → load returns empty, no crash
- compaction re-save when stale entries dropped (regression for the af191a0 bug)
Plus a few more covering window-age pruning and theinitializevoted_ids intersection.
-
Can we move
resolved_blockback? -
Can you update stale class docstring for upkeeping?
swap_tracker.py:14–23 still says:
"The scoring window populates naturally as swaps complete."
Now outdated, we can update to mention store-backed restoration
Other:
- Cache path uses
config.neuron.full_pathvsPendingConfirmQueue's~/.allways/validator/.
Good to go after
|
Implemented ✅
Validation results:
|
PunchTheDev
left a comment
There was a problem hiding this comment.
hey, I think there's still a gap in the persistence wiring that defeats the stated invariant:
resolve()doesn't persist —SwapTracker.resolve()atswap_tracker.py:70is the primary "move a swap into the scoring window" path, called fromforward.py:250and:322on every confirm and timeout vote. It mutateswindowandvoted_idsbut never calls_persist(). A crash betweenresolve()and the nextprune_window/_poll_innerthat happens to persist for another reason will lose the resolved swap — which is the exact scenario this PR exists to prevent. The docstring currently says "persistence hooks at every state mutation point," so this should be one, right?resolved_blockis still duplicated — my last review asked to consolidate this._resolved_blockis back inswap_tracker.py:194(good) butresolved_blockalso still lives inscoring_store.py:148doing the same thing. Please pick one home and have the other import it.- missing the load-bearing regression test — the store tests are solid, but none of them cover
resolve()→ new tracker → window restored. That's the single most valuable test for this PR's invariant and would have caught the bullet above.
LandynDev
left a comment
There was a problem hiding this comment.
Can you address requested changes left by @PunchTheDev ?
and let me know thoughts on point 1
Valid catch. There was a real crash window between voting and the next incidental persist. I fixed it. |
Closes #16
Summary
ScoringWindowStorefor atomic JSON persistence of the scoring window and voted set, following the existing tmp-file-then-rename pattern used bySwapFulfiller._save_sent_cache.SwapTrackerload oninitialize(), persist on swap resolution, window pruning, andmark_voted()._resolved_blockhelper by sharing a singleresolved_block()from the store module.<neuron.full_path>/scoring_window.json.Design
Persistence hooks at every state mutation point only. The store is optional (
Nonedisables persistence) so existing tests and standalone usage remain unaffected. On cold start, stale voted IDs are pruned against the active swap set to prevent unbounded growth.Test plan
ruff check allways/validator/scoring_store.py allways/validator/swap_tracker.py neurons/validator.pypassespython3 -m pytest tests/test_rate.py tests/test_chains.py -q --tb=shortpassespython3 -m pytest tests/test_scoring_store.py -q --tb=shortpasses (local)SCORING_WINDOW_BLOCKSpruned on load