fix(xmtp_mls,xmtp_proto): durable AddMissingInstallations reconciliation on the TaskRunner - #3897
Conversation
How to use the Graphite Merge QueueAdd the label mergequeue to this PR to add it to the merge queue. You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. |
|
Claude finished @insipx's task —— View job PR Review - 2026-07-24 20:30 UTCOverviewStrong architectural improvement that moves installation reconciliation from fragile one-shot inline execution to durable task-based retry with backoff. The root cause diagnosis is clear, the fix is well-tested, and the implementation follows established patterns from the KpMaintenance work. Key Observations✅ Strengths:
🔍 Areas warranting attention:
RecommendationsPriority 1 (addresses Tyler's feedback):
Priority 2 (test coverage):
Priority 3 (observability):
VerdictArchitecture is sound, implementation follows best practices, and the tests validate the happy path. Tyler's points #1 and #2 deserve follow-up (either as changes or documented rationale), and the No blocking issues for merge once Tyler's concerns are addressed. |
ApprovabilityVerdict: Needs human review Unable to check for correctness in f841511. This PR changes the device sync reconciliation from inline execution to durable task-based execution with retry/backoff. While the author owns all changed files and the change is well-tested, the architectural shift in execution model and timing semantics warrants human review. You can customize Macroscope's approvability policy. Learn more. |
1 similar comment
ApprovabilityVerdict: Needs human review Unable to check for correctness in f841511. This PR changes the device sync reconciliation from inline execution to durable task-based execution with retry/backoff. While the author owns all changed files and the change is well-tested, the architectural shift in execution model and timing semantics warrants human review. You can customize Macroscope's approvability policy. Learn more. |
ApprovabilityVerdict: Needs human review This PR fundamentally changes installation reconciliation from inline synchronous processing to durable task-based execution with retry/backoff. While the author owns all changed files and the fix improves reliability, the architectural changes to core device sync behavior warrant human review. You can customize Macroscope's approvability policy. Learn more. |
… TaskRunner A new installation's membership adds were a one-shot inline call in the DeviceSync worker's welcome handler; per-group failures were swallowed (queue_for_each warn-and-drop) and the triggering event is never re-fired, so one lost race (e.g. MissingSequenceId racing identity propagation) permanently skipped the add — root cause of the DeviceSync sendSyncRequest CI flake. The welcome handler now enqueues one durable AddMissingInstallations task per eligible group (deduped by payload hash); the TaskRunner executes the idempotent MlsGroup::add_missing_installations with the existing backoff/retry machinery.
f841511 to
dd2c05c
Compare
|
Blocked on xmtp/proto#341 — this PR's generated-code changes for the 🤖 Generated with Claude Code |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3897 +/- ##
==========================================
- Coverage 85.77% 85.75% -0.03%
==========================================
Files 427 427
Lines 68201 68196 -5
==========================================
- Hits 58501 58483 -18
- Misses 9700 9713 +13 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Dismissing prior approval to re-evaluate dd2c05c
|
@insipx three clarifications from the diagnosis on
#3895 stays complementary: each task attempt's |
…onciliation Review follow-ups (tylerhawkes on #3897): the sync-group creation branch persisted the group row before its single inline add — a failure there was never re-attempted since later calls take the Some branch; enqueue the durable reconcile task first as a crash-safe safety net. And the worker's event loop exited on broadcast Lagged, dropping unseen NewSyncGroupFromWelcome events with no task row created; recover level-triggered by re-running the deduped scheduler (plus a message sweep) and continuing the loop.
Dismissing prior approval to re-evaluate 15dd817
Durable TaskRunner intent used by libxmtp's device-sync worker: when a sync-group welcome signals a new installation, one task per eligible group reconciles membership with the inbox's latest identity state, retried with backoff instead of a one-shot inline add (xmtp/libxmtp#3897). Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
3d3b930 to
720c68c
Compare
…ngInstallations xmtp/proto#341 merged (a9b4d561); dev/gen_protos.sh from that rev produces zero diff against the checked-in generated files — only the pin moves.
The safety-net enqueue's own error path had recreated the hole it was closing: an early return after create_and_insert skipped the inline add on an already-persisted row. The inline add now runs regardless; if both halves fail, the enqueue is re-attempted so the durable path can heal.
720c68c to
f9d0f56
Compare
This comment has been minimized.
This comment has been minimized.
Dismissing prior approval to re-evaluate a69b5f0
An empty element selection produces an empty archive, so the assertions depended entirely on the membership-add welcome path — which converges too slowly on loaded CI runners even now that it retries durably. Requesting Messages+Consent makes every sendSyncRequest/sendSyncArchive retry attempt self-sufficient through the archive import path, and makes the tests exercise the round trip they are named for.
… add fails Enqueue-first ran a duplicate reconcile at every sync-group creation — an extra identity-updates fetch that broke the pinned network-call counts on three platforms (create_client_does_not_hit_network, Android/iOS testNetworkDebugInformation). The durable task is now armed from the inline add's error path only, restoring the exact prior happy-path network profile while keeping the durable heal. Trade-off: a process crash between create_and_insert and the inline add completing is no longer covered — a ms-scale window, once per installation.
a69b5f0 to
761b197
Compare
Problem
The node-sdk
DeviceSync > should sync messages across installations using sendSyncRequest and syncAllDeviceSyncGroupstest has been failing most CI runs since Jul 23 (10 of 14 node-sdk jobs across main and several branches), always with the same signature: the group never appears on the second installation for the full 90s poll, while the same code sometimes converges in ~3s.Root cause: when a new installation registers, the DeviceSync worker's
NewSyncGroupFromWelcomehandler ranadd_new_installation_to_groups()inline, exactly once:queue_for_eachwere warn-logged and swallowed (groups/intents/queue.rs), so a single lost race (e.g.MissingSequenceIdwhen the membership add races identity propagation — see the diagnosis ontyler/seqid-retryable) silently skipped the add;elements: []the archive round trip is empty, so the welcome path is the only delivery path).Retryability classification alone (
tyler/seqid-retryable) can't fix this: the queue-time errors are dropped regardless of classification, and an abandoned publish leaves the intent dormant with nothing to re-trigger it.Fix
Make the reconciliation durable: the welcome handler now enqueues one
AddMissingInstallations { group_id }task per eligible group on the TaskRunner (create_or_ignore_task, deduped by payload hash, then a wake). The TaskRunner arm loads the group and runs the existing idempotentMlsGroup::add_missing_installations(); failures ride the standard task backoff (2s ×1.5 capped at 60s, 20 attempts, 3-day expiry), missing/malformed groups delete the task.xmtp_proto: newTaskoneof variantAddMissingInstallations(tag 7) hand-added to the checked-in generated files ahead of the upstream xmtp/proto PR (same pattern as the earlier KpMaintenance work). Wire encoding pinned indata_hash_encoding_is_pinned.xmtp_mls:schedule_add_installations_to_groups()replaces the inline bulk add; new TaskRunner arm;queue_for_each(now dead, and the error-swallowing culprit) removed.test_only_added_to_correct_groupspolls for convergence (the metric now fires at schedule time), andtest_has_same_sync_group_asconverges on epoch too — the post-join KeyUpdate commit riding the first HMAC-cycle message landed deterministically inside its old assert window.Verification
xmtp_mls707/707,xmtp_db+xmtp_proto322/322 (cargo nextest, ci profile)just lint-rustcleanDeviceSync.test.ts(including the exact failing CI test) 3/3 green against freshly built bindingsFollow-ups
dev/gen_protos.shregen +proto_versionbump.elements: [], which produces an empty archive; requestingMessageswould make each retry attempt self-sufficient.🤖 Generated with Claude Code
Note
Add durable
AddMissingInstallationstask reconciliation to the TaskRunnerAddMissingInstallationsproto task variant and wires it into theTaskWorkerdispatch loop, replacing inline membership adds with retryable, deduplicated durable tasks.add_missing_installations()fails during sync group creation inDeviceSyncClient, a durable task is scheduled instead of silently dropping the add.SyncWorker.evt_new_sync_group_from_welcomenow schedules tasks viaschedule_add_installations_to_groupsrather than executing inline adds, and recovers from broadcast receiver lag by re-scheduling reconciliation.TaskWorkerdeletesAddMissingInstallationstasks permanently on malformed group ID or group not found, and retries on transient errors.QueueIntentBuilder::queue_for_eachis removed; callers must use the new task-based scheduling path.Macroscope summarized 761b197.