Skip to content

refactor(ops): decouple task-per-tx drivers from SubOperationTracker (#1454 phase 3c)#3889

Merged
iduartgomez merged 1 commit intomainfrom
phase-3c-decouple-sub-op-tracker
Apr 16, 2026
Merged

refactor(ops): decouple task-per-tx drivers from SubOperationTracker (#1454 phase 3c)#3889
iduartgomez merged 1 commit intomainfrom
phase-3c-decouple-sub-op-tracker

Conversation

@iduartgomez
Copy link
Copy Markdown
Collaborator

Problem

After Phases 3a (#3843) and 3b (#3884) migrated client-initiated PUT and GET to task-per-tx drivers, both drivers still registered subscribe children with SubOperationTracker via expect_and_register_sub_operation. This was only needed so op_manager.is_sub_operation(tx) (a DashMap lookup) returned true at 4 guard sites that prevent spurious client notifications. The task-per-tx drivers never used the tracker's parking-lot mechanism — they await or fire-and-forget children directly.

Additionally, async non-blocking subscribe children (track_parent=false) were never registered in the tracker, so op_manager.is_sub_operation() returned false for them — they'd incorrectly try to notify a nonexistent client waiter at LocalSubscribeComplete.

Solution

Switch all 4 is_sub_operation guard sites from op_manager.is_sub_operation(tx) (DashMap lookup on SubOperationTracker.parent_of) to tx.is_sub_operation() (structural check: Transaction.parent.is_some(), set at creation by new_child_of). This is a superset — correct for both legacy and task-per-tx children, and fixes the latent bug for async non-blocking children.

Then remove expect_and_register_sub_operation calls from PUT and GET task-per-tx drivers, making them fully independent of SubOperationTracker.

SubOperationTracker itself stays alive — legacy PUT/GET relay paths still use the parking-lot mechanism. Full deletion deferred until relay migration (#3883) completes.

Guard sites changed

File Context
p2p_protoc.rs:2061 LocalSubscribeComplete silent-absorb
node.rs:552 Subscribe completion — skip client notify for children
node.rs:646 Error path — skip client error for children
subscribe.rs:932 handle_abort — sub-op vs standalone abort routing

Testing

  • cargo clippy -- -D warnings — clean
  • cargo test -p freenet — 2380 tests pass (4 pre-existing macOS connectivity failures unrelated)
  • Source-scrape test updated to reflect removed tracker registration

Fixes

Partial progress on #1454 (Phase 3c)

…1454 phase 3c)

Switch all 4 is_sub_operation guard sites (p2p_protoc.rs, node.rs x2,
subscribe.rs) from op_manager.is_sub_operation() (DashMap lookup) to
Transaction::is_sub_operation() (structural parent field check). This
is a superset — correct for both legacy and task-per-tx children.

Remove expect_and_register_sub_operation calls from PUT and GET
task-per-tx drivers (op_ctx_task.rs). These drivers await or
fire-and-forget children directly and never use the parking-lot
mechanism. The tracker registration was only needed for the DashMap
guard, which is now replaced by the structural check.

Latent fix: async non-blocking subscribe children (track_parent=false)
were never registered in the tracker, so op_manager.is_sub_operation()
returned false — they'd incorrectly try to notify a nonexistent client
waiter at LocalSubscribeComplete. The structural check fixes this.

SubOperationTracker itself stays alive for legacy PUT/GET relay paths
that still use the parking-lot. Full deletion deferred until relay
migration (#3883) completes.
@github-actions
Copy link
Copy Markdown
Contributor

Rule Review: Clean refactor with one minor docstring inaccuracy

Rules checked: operations.md, code-style.md, testing.md, git-workflow.md
Files reviewed: 7 (.claude/rules/operations.md, node.rs, p2p_protoc.rs, op_state_manager.rs, get/op_ctx_task.rs, put/op_ctx_task.rs, subscribe.rs)


Warnings

None.


Info

  • crates/core/src/node/op_state_manager.rs:196–199 — The new docstring on SubOperationTracker::is_sub_operation reads "This method remains for legacy relay paths and unit tests" but the method is #[cfg(test)], making it inaccessible to production relay-path code. The phrasing implies production use that can't exist. Should read "This method remains for unit tests of legacy relay paths" (code-style.md: doc comments should accurately describe what the code does).

Summary of changes reviewed: The PR correctly replaces four op_manager.is_sub_operation() call-sites (DashMap lookup via SubOperationTracker) with Transaction::is_sub_operation() (structural parent.is_some() check). The structural check is a strict superset — it works for both legacy and task-per-tx children, including the latent bug class of async non-blocking subscribe children that were never registered in the tracker. The expect_and_register_sub_operation removals from PUT and GET task-per-tx drivers are correct: those drivers never used the parking-lot mechanism that registration was designed to feed. The operations.md update and line-number reference (operations.rs:182–208) are accurate against the current file. The removed test assertions (ordering of expect_and_register_sub_operation) are correctly dropped since the call they guarded no longer exists.


Rule review against .claude/rules/. WARNING findings block merge.

@iduartgomez iduartgomez added this pull request to the merge queue Apr 16, 2026
Merged via the queue into main with commit 9efe16a Apr 16, 2026
11 checks passed
@iduartgomez iduartgomez deleted the phase-3c-decouple-sub-op-tracker branch April 16, 2026 12:57
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.

1 participant