Skip to content

refactor: simplify FiltersManager.start_download#450

Merged
xdustinface merged 1 commit intov0.42-devfrom
refactor/filters-start-logic
Feb 18, 2026
Merged

refactor: simplify FiltersManager.start_download#450
xdustinface merged 1 commit intov0.42-devfrom
refactor/filters-start-logic

Conversation

@xdustinface
Copy link
Collaborator

@xdustinface xdustinface commented Feb 18, 2026

  • remove init/extend_target blocks in start_download() and just always call init() since there shouldn't be any inflight state when start_download is called.
  • add is_idle() helper to FiltersManager and FiltersPipeline to verify this via debug_assert
  • remove FiltersPipeline::active_count() since its dead code after removing the relevant log

Summary by CodeRabbit

  • Refactor

    • Simplified download initialization to remove conditional branches and make startup behavior consistent.
    • Consolidated idle-state checks into a single detection mechanism for clearer synchronization flow.
    • Eliminated complex batch-preservation and conditional pipeline reinitialization logic.
  • Bug Fixes

    • More reliable startup/resume behavior and fewer synchronization edge cases due to improved idle detection and initialization.
  • Tests

    • Added tests validating idle detection and state transitions.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 18, 2026

📝 Walkthrough

Walkthrough

Refactors filter sync initialization and idle-state detection: introduces is_idle() checks in manager and pipeline, simplifies start_download to always initialize the pipeline for the remaining range, consolidates startup logging, and adds tests covering idle-state behavior and transitions.

Changes

Cohort / File(s) Summary
Filter Manager Initialization Simplification
dash-spv/src/sync/filters/manager.rs
Added private is_idle() helper; added debug_assert that manager is idle on download start; always sets next_batch_to_store to download_start; consolidated startup logging; simplified start_download to unconditionally initialize the pipeline for the remaining range or a stored-only scan when ahead, removing previous in-flight branching and resume logic.
Pipeline Idle-State Refactoring & Tests
dash-spv/src/sync/filters/pipeline.rs, dash-spv/src/sync/filters/.../tests.rs
Replaced pub(super) fn active_count(&self) -> usize with pub(super) fn is_idle(&self) -> bool; updated call sites/tests to use is_idle() or coordinator.active_count() where appropriate; added tests validating is_idle and manager clear_in_flight_state transitions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hopped through code with tiny paws,
I scrubbed the branches, fixed the claws,
Now downloads start with clearer light,
Idle checks snug, the pipeline's right.
A joyful nibble, changes now applause! 🎉

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly describes the main refactoring objective: simplifying the start_download method by removing conditional logic and adding idle state detection.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/filters-start-logic

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
dash-spv/src/sync/filters/pipeline.rs (1)

83-86: Consider adding a dedicated unit test for is_idle().

The implementation is correct — completed_batches need not be included because it represents consumed output (cleared by init()), and batch_trackers is always empty when both coordinator counts are zero (trackers are removed on completion or retry-exhaustion, and re-queued items increment pending_count). However, the new method has no direct unit test in this file; all tests continue to call pipeline.coordinator.active_count() directly. A focused test would lock in the combined active + pending semantic.

🧪 Suggested test skeleton
#[test]
fn test_pipeline_is_idle_initial() {
    let pipeline = FiltersPipeline::new();
    assert!(pipeline.is_idle());
}

#[test]
fn test_pipeline_is_idle_with_pending() {
    let mut pipeline = FiltersPipeline::new();
    pipeline.init(0, 999);
    // pending_count > 0 → not idle
    assert!(!pipeline.is_idle());
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dash-spv/src/sync/filters/pipeline.rs` around lines 83 - 86, Add focused unit
tests exercising FiltersPipeline::is_idle(): create a FiltersPipeline via
FiltersPipeline::new() and assert is_idle() is true initially, then create a
pipeline, call init(0, 999) (or another call that increases pending_count) and
assert is_idle() is false; include both scenarios in the test module so the
combined active+pending semantic is verified rather than calling
coordinator.active_count() directly.
dash-spv/src/sync/filters/manager.rs (1)

103-110: LGTM — filter_pipeline.is_idle() omitting completed_batches is safe here.

pipeline.is_idle() only inspects the coordinator's active/pending counts. In theory, filter_pipeline.completed_batches could be non-empty while returning true (if take_completed_batches() hasn't been called yet). In practice this window is unreachable: start_download is only called from handle_new_filter_headers when the state is WaitingForConnections | WaitForEvents, which is always preceded by clear_in_flight_state() (full pipeline reset). Even if reached, init() in start_download clears completed_batches unconditionally.

One minor gap: there is no dedicated unit test for is_idle(). Given its role as the basis of the debug_assert, a few edge-case tests (fresh manager, partially in-flight, after clear_in_flight_state) would be valuable.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dash-spv/src/sync/filters/manager.rs` around lines 103 - 110, is_idle
currently checks active_batches, blocks_remaining, filters_matched,
pending_batches and filter_pipeline.is_idle() but there are no unit tests
ensuring its correctness across edge cases; add unit tests for Manager::is_idle
that cover a fresh manager, a partially in-flight state (populate
active_batches/pending_batches/filters_matched as appropriate and assert is_idle
== false), and the state after calling clear_in_flight_state() (ensure
completed_batches behavior does not make is_idle return false unexpectedly).
Reference Manager::is_idle, FilterPipeline::is_idle,
FilterPipeline::take_completed_batches, Manager::clear_in_flight_state,
Manager::start_download and handle_new_filter_headers when designing tests to
simulate the lifecycle transitions that could affect completed_batches.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@dash-spv/src/sync/filters/manager.rs`:
- Around line 103-110: is_idle currently checks active_batches,
blocks_remaining, filters_matched, pending_batches and filter_pipeline.is_idle()
but there are no unit tests ensuring its correctness across edge cases; add unit
tests for Manager::is_idle that cover a fresh manager, a partially in-flight
state (populate active_batches/pending_batches/filters_matched as appropriate
and assert is_idle == false), and the state after calling
clear_in_flight_state() (ensure completed_batches behavior does not make is_idle
return false unexpectedly). Reference Manager::is_idle, FilterPipeline::is_idle,
FilterPipeline::take_completed_batches, Manager::clear_in_flight_state,
Manager::start_download and handle_new_filter_headers when designing tests to
simulate the lifecycle transitions that could affect completed_batches.

In `@dash-spv/src/sync/filters/pipeline.rs`:
- Around line 83-86: Add focused unit tests exercising
FiltersPipeline::is_idle(): create a FiltersPipeline via FiltersPipeline::new()
and assert is_idle() is true initially, then create a pipeline, call init(0,
999) (or another call that increases pending_count) and assert is_idle() is
false; include both scenarios in the test module so the combined active+pending
semantic is verified rather than calling coordinator.active_count() directly.

- remove `init`/`extend_target` blocks in `start_download()` and just always call `init()` since there shouldn't be any inflight state when `start_download` is called.
- add `is_idle()` helper to `FiltersManager` and `FiltersPipeline` to verify this via `debug_assert`
- remove `FiltersPipeline::active_count()` since its dead code after removing the relevant log
@xdustinface xdustinface force-pushed the refactor/filters-start-logic branch from 31e835f to 24f52f9 Compare February 18, 2026 01:26
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
dash-spv/src/sync/filters/pipeline.rs (2)

84-85: is_idle() silently passes when completed-but-unconsumed batches exist.

completed_batches is populated by receive_with_data() after coordinator.receive() is called, so once all in-flight batches finish, coordinator.active_count() == 0 && coordinator.pending_count() == 0 is satisfied while completed_batches may still hold data that take_completed_batches() hasn't consumed yet. The debug_assert!(self.is_idle()) in manager.rs::start_download would not catch this case.

In practice this isn't a bug — clear_in_flight_state() creates a fresh FiltersPipeline::new() (dropping completed_batches) and init() also explicitly clears completed_batches. But including the field in the predicate would make the invariant airtight:

🛡️ Proposed strengthening of `is_idle()`
 pub(super) fn is_idle(&self) -> bool {
-    self.coordinator.active_count() == 0 && self.coordinator.pending_count() == 0
+    self.coordinator.active_count() == 0
+        && self.coordinator.pending_count() == 0
+        && self.completed_batches.is_empty()
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dash-spv/src/sync/filters/pipeline.rs` around lines 84 - 85, The is_idle()
predicate currently only checks coordinator.active_count() and
coordinator.pending_count(), but it should also ensure there are no unconsumed
completed batches; update FiltersPipeline::is_idle() to include a check that
self.completed_batches is empty (e.g., add &&
self.completed_batches.is_empty()), so the invariant used by
manager.rs::start_download (debug_assert!(self.is_idle())) correctly fails when
receive_with_data() has populated completed_batches that
take_completed_batches() hasn't consumed yet.

326-333: test_is_idle only covers two of the three lifecycle states.

The test verifies true (empty) and false (after init), but not the round-trip: after all filters are received and take_completed_batches() is called, is_idle() should return true again. Adding that leg would catch any future regression in coordinator.receive() bookkeeping.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dash-spv/src/sync/filters/pipeline.rs` around lines 326 - 333, Extend the
test_is_idle unit test to cover the third lifecycle state: after creating
FiltersPipeline::new() and calling pipeline.init(...), simulate receiving all
filters via the coordinator.receive(...) path (use the actual
FiltersCoordinator/receive method used in the module) so the pipeline reaches
completion, call pipeline.take_completed_batches(), and assert
pipeline.is_idle() returns true again; refer to the existing test name
test_is_idle and the methods FiltersPipeline::init, FiltersPipeline::is_idle,
FiltersPipeline::take_completed_batches and FiltersCoordinator::receive to
locate where to add the final leg.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@dash-spv/src/sync/filters/pipeline.rs`:
- Around line 84-85: The is_idle() predicate currently only checks
coordinator.active_count() and coordinator.pending_count(), but it should also
ensure there are no unconsumed completed batches; update
FiltersPipeline::is_idle() to include a check that self.completed_batches is
empty (e.g., add && self.completed_batches.is_empty()), so the invariant used by
manager.rs::start_download (debug_assert!(self.is_idle())) correctly fails when
receive_with_data() has populated completed_batches that
take_completed_batches() hasn't consumed yet.
- Around line 326-333: Extend the test_is_idle unit test to cover the third
lifecycle state: after creating FiltersPipeline::new() and calling
pipeline.init(...), simulate receiving all filters via the
coordinator.receive(...) path (use the actual FiltersCoordinator/receive method
used in the module) so the pipeline reaches completion, call
pipeline.take_completed_batches(), and assert pipeline.is_idle() returns true
again; refer to the existing test name test_is_idle and the methods
FiltersPipeline::init, FiltersPipeline::is_idle,
FiltersPipeline::take_completed_batches and FiltersCoordinator::receive to
locate where to add the final leg.

@xdustinface xdustinface merged commit a05d256 into v0.42-dev Feb 18, 2026
53 checks passed
@xdustinface xdustinface deleted the refactor/filters-start-logic branch February 18, 2026 20:44
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.

2 participants

Comments