Skip to content

Refactor method scheduling into ExecutionGroup#12

Open
mark1russell7 wants to merge 1 commit into
feature/method-max-retriesfrom
refactor/execution-group
Open

Refactor method scheduling into ExecutionGroup#12
mark1russell7 wants to merge 1 commit into
feature/method-max-retriesfrom
refactor/execution-group

Conversation

@mark1russell7

Copy link
Copy Markdown

Summary

Refactors ddp-client's method scheduling into an explicit ExecutionGroup model. This is the refactor half of #10, rebuilt on top of #11 (maxRetries) — it supersedes #10, which can be closed.

Stacked on #11 (feature/method-max-retries) — merge that first; this diff assumes it.

Architecture

Three responsibilities, three owners (previously smeared across MethodInvoker, the _outstandingMethodBlocks array, and the global _methodsBlockingQuiescence map):

  • MethodInvoker — transport lifecycle of one method as an explicit state machine (PENDING → IN_FLIGHT → WAITING_FOR_RESEND → COMPLETE/ABORTED), including the retry budget (noRetry/maxRetries) consumed at re-send time. Every terminal transition reports to the connection through a single onComplete delegate — no matter how a method ends (completed, aborted on disconnect, retry budget exhausted), connection bookkeeping runs in exactly one place.
  • ExecutionGroup (new) — owns the DDP two-phase completion protocol per group: result tracking, updated tracking (quiescence), data visibility, and deciding when each invoker's callback fires. Replaces the {wait, methods} block objects; "wait" methods become atomic groups.
  • Connection — schedules groups sequentially (_methodQueue), advances when the leading group has fully delivered (reaping any groups emptied while queued), and merges old groups into new ones on reconnect with completion state preserved (transferMethod).

Fixes over the #10 version

Review of #10 found three defects, all fixed here with regression tests:

  1. Half-finished methods could hang forever after reconnect. Feature/retries and refactors #10 narrowed the post-reconnect force-complete and updated/data-visibility routing to _methodQueue[0] — but after a reconnect merge, a method that received its result before the drop can sit in a later group (e.g. when onReconnect queues a wait login ahead of it). It would never complete — the exact hang class Fix DDP method hang on disconnect and null socket in Session.close() meteor/meteor#14193 fixes. All routing now finds the method's owning group, and the post-reconnect flush covers every group. Regression test: "result received before reconnect completes after onReconnect wait method".
  2. Teardown could send queued methods on a dead connection. The retry: false mass-abort cleared nothing first, so completion bookkeeping would advance the queue and sendMessage() later groups mid-teardown. The queue is now cleared before aborting (same ordering Fix DDP method hang on disconnect and null socket in Session.close() meteor/meteor#14193 established). Regression test: "disconnect with retry false does not send queued methods".
  3. The invoker's onComplete delegate was passed but never wired, leaving three call sites each responsible for remembering manual cleanup. It's now the single notification path; the manual cleanup calls are gone.

Also: retry exhaustion keeps #11's invocation-failed error code (maxRetries: 0noRetry, unchanged error handling for callers), the async onReconnect callback support from release-3.5.1 is preserved (Promise.allSettled before re-sending), and the loud failure on an updated for an unknown method is restored.

Behavior notes

  • Methods called while a post-reconnect recovery group is in flight now queue behind it instead of interleaving into it. No API change; slight ordering difference on reconnect.
  • White-box tests referencing _outstandingMethodBlocks / block.wait are renamed to _methodQueue / group.atomic.

Tests

Splits method handling across three explicit owners: MethodInvoker is a
transport state machine (PENDING -> IN_FLIGHT -> WAITING_FOR_RESEND ->
COMPLETE/ABORTED) that consumes the retry budget at re-send time and
reports every terminal transition through a single onComplete delegate;
the new ExecutionGroup owns two-phase completion (result, updated, data
visibility) per group of methods, replacing the {wait, methods} blocks
and the global _methodsBlockingQuiescence map; the connection schedules
groups sequentially and merges old groups into new ones on reconnect,
preserving completion state.

Post-reconnect completion routing covers methods in any group (a method
whose result arrived before the drop can sit behind an onReconnect wait
method), teardown clears the queue before mass-aborting so nothing is
sent on a dead connection, and empty groups are reaped when advancing.
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.

1 participant