Skip to content

Conversation

@richardhuaaa
Copy link
Contributor

@richardhuaaa richardhuaaa commented Oct 31, 2025

This fixes an issue found via @cameronvoell's validation: after a forked client successfully recovers from their forked state, they seem to remain evaluating themselves as forked, and continue to issue re-add requests with each worker iteration after that.

There are two fixes here:

  1. After a readd welcome is received, we need to update not only the readd_status, but also the is_forked flag. This fixes the bulk of the issue.
  2. When checking for forks, we need to ignore local commit log entries from before the latest Welcome was applied. This fixes any edge cases around stale data after the group has been reinitialized.

Also included an end-to-end unit test that validates both of these fixes (fails without either one of the fixes).

(1) was not previously mentioned in the XIP, also updated it to include this.

Copy link
Contributor Author

richardhuaaa commented Oct 31, 2025


How to use the Graphite Merge Queue

Add 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.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@claude
Copy link

claude bot commented Oct 31, 2025

Claude Code is working…

I'll analyze this and get back to you.

View job run

@richardhuaaa richardhuaaa force-pushed the 10-31-fix_fork_after_readd branch from 8aa78eb to b06561a Compare November 5, 2025 00:56
@richardhuaaa richardhuaaa force-pushed the 10-31-fix_fork_after_readd branch from b06561a to df6dab3 Compare November 5, 2025 01:09
@richardhuaaa richardhuaaa marked this pull request as ready for review November 5, 2025 01:12
@richardhuaaa richardhuaaa requested a review from a team as a code owner November 5, 2025 01:12
@macroscopeapp
Copy link
Contributor

macroscopeapp bot commented Nov 5, 2025

Fix commit log fork after readd by resetting visible commits at the latest Welcome and clearing forked status when the current installation is readded in xmtp_mls

Update commit log retrieval to start from the latest Welcome at or after the cursor, add fork-state clearing when the current installation is readded, and extend tests to cover end-to-end fork and readd flows. The change adjusts local commit log queries, propagates the caller installation id to MlsGroup::mark_readd_requests_as_responded, and adds targeted tracing for fork evaluation and remote publish paths.

📍Where to Start

Start with DbConnection<QueryLocalCommitLog>::get_local_commit_log_after_cursor in local_commit_log.rs, then review MlsGroup::mark_readd_requests_as_responded in mls_sync.rs and the end-to-end test in test_commit_log_e2e.rs.


📊 Macroscope summarized da27a02. 5 files reviewed, 18 issues evaluated, 18 issues filtered, 0 comments posted. View details

@codecov
Copy link

codecov bot commented Nov 5, 2025

Codecov Report

❌ Patch coverage is 85.36585% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.50%. Comparing base (62f07b0) to head (da27a02).

Files with missing lines Patch % Lines
xmtp_mls/src/groups/commit_log.rs 40.00% 6 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2706   +/-   ##
=======================================
  Coverage   77.50%   77.50%           
=======================================
  Files         367      367           
  Lines       53872    53907   +35     
=======================================
+ Hits        41753    41782   +29     
- Misses      12119    12125    +6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@richardhuaaa richardhuaaa changed the title fix fork after readd Fix fork state after recovery is successful Nov 5, 2025
));
}
let after_cursor = after_cursor as i32;
let latest_welcome_rowid: Option<i32> = self.raw_query_read(|db| {
Copy link
Contributor

Choose a reason for hiding this comment

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

after_cursor as i32 + 1 can overflow when after_cursor == i32::MAX, causing panic or wrong filtering. Consider avoiding the addition: if no Welcome is found, use dsl::rowid.gt(after_cursor as i32); otherwise use dsl::rowid.ge(latest_welcome_rowid).

-        let latest_welcome_rowid: Option<i32> = self.raw_query_read(|db| {
+        let latest_welcome_rowid: Option<i32> = self.raw_query_read(|db| {
             dsl::local_commit_log
                 .select(diesel::dsl::max(dsl::rowid))
                 .filter(dsl::group_id.eq(group_id))
                 .filter(dsl::rowid.gt(after_cursor as i32))
                 .filter(dsl::commit_type.eq(Some("Welcome".to_string())))
                 .first(db)
         })?;
-        let gte_cursor = latest_welcome_rowid.unwrap_or(after_cursor as i32 + 1);
-
-        let query = dsl::local_commit_log
-            .filter(dsl::group_id.eq(group_id))
-            .filter(dsl::rowid.ge(gte_cursor))
-            .filter(dsl::commit_sequence_id.ne(0));
+        let base_query = dsl::local_commit_log
+            .filter(dsl::group_id.eq(group_id))
+            .filter(dsl::commit_sequence_id.ne(0));
+
+        let query = if let Some(welcome_rowid) = latest_welcome_rowid {
+            base_query.filter(dsl::rowid.ge(welcome_rowid))
+        } else {
+            base_query.filter(dsl::rowid.gt(after_cursor as i32))
+        };
 
         let rows = self.raw_query_read(|db| match order {
             LocalCommitLogOrder::AscendingByRowid => query.order_by(dsl::rowid.asc()).load(db),
             LocalCommitLogOrder::DescendingByRowid => query.order_by(dsl::rowid.desc()).load(db),
         })?;
 
         Ok(rows)

🚀 Reply to ask Macroscope to explain or update this suggestion.

👍 Helpful? React to give us feedback.

.filter(dsl::commit_type.eq(Some("Welcome".to_string())))
.first(db)
})?;
let gte_cursor = latest_welcome_rowid.unwrap_or(after_cursor as i32 + 1);
Copy link
Contributor

Choose a reason for hiding this comment

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

this could use saturating add even though it's unlikely to ever get that far.

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.

4 participants