Skip to content

Conversation

@insipx
Copy link
Contributor

@insipx insipx commented Nov 13, 2025

Try to order envelopes by their dependencies and resolve any that are missing. if resolution fails, drop the missing envelopes and continue with the query.

the ordering extension is modified again in #2775 to remove envelopes from missing. i may backport those changes to this pr for consistency

@insipx insipx requested a review from a team as a code owner November 13, 2025 14:19
This was referenced Nov 13, 2025
@insipx
Copy link
Contributor Author

insipx commented Nov 13, 2025

@claude
Copy link

claude bot commented Nov 13, 2025

Claude finished @insipx's task —— View job


Code Review - PR #2771

Review Date: November 13, 2025, 22:28 UTC


Critical Issues

1. Infinite Loop Risk in order() (xmtp_api_d14n/src/protocol/order.rs:41)

The while let Some(missing) = sort::causal(...).sort()? loop at line 41 lacks termination guarantees. If resolver.resolve(cursors) returns envelopes that don't satisfy the missing dependencies, the loop will continue indefinitely. The code needs progress detection:

// Current code has no way to detect:
// - resolver returns empty vec repeatedly
// - resolver returns envelopes that don't resolve the missing dependencies
// - resolver returns the same envelopes we already have

Recommendation: Track progress between iterations (e.g., count of missing envelopes, hash of missing set) and break/error if no progress is made after resolution attempt.


High Priority Issues

2. Missing Thread Safety Bounds (xmtp_api_d14n/src/protocol/traits/envelopes.rs:17, 39)

As noted by @tylerhawkes, the ProtocolEnvelope and Envelope traits should have MaybeSend + MaybeSync bounds for consistency with other traits in the codebase. Currently:

  • Line 17: ProtocolEnvelope only requires std::fmt::Debug
  • Line 39: Envelope only requires std::fmt::Debug

But ResolveDependencies (line 25 of dependency_resolution.rs) already uses these bounds, creating inconsistency.


Medium Priority Issues

3. Silently Dropping Envelopes on Resolution Failure (order.rs:60-61)

When ResolutionError::ResolutionFailed occurs, the code returns Ok(()) and silently drops all missing envelopes. While the PR description mentions this is intentional ("drop the missing envelopes and continue"), this could mask critical data loss scenarios. Consider:

  • Logging dropped envelopes at warn/error level
  • Returning metadata about what was dropped
  • Exposing metrics for monitoring

4. Error Handling Ambiguity (order.rs:56-64)

The resolver returns ResolutionError::ResolutionFailed to signal "couldn't find dependencies", but this same error type is used for other failures. The error handling would be clearer with distinct error variants for "dependencies not found" vs "network/API failure".

5. Test Coverage (order.rs:76)

The TODO comment at line 76 indicates missing tests for the critical Ordered::order() function. Given the complexity of the async resolution loop and the infinite loop risk, comprehensive tests are essential. The 0% patch coverage reported by Codecov confirms this gap.


Low Priority / Style

6. Splice Performance (order.rs:68-69)

Using splice(0..0, ...) twice to prepend elements is less efficient than alternatives:

// Current approach requires shifting elements twice
envelopes.splice(0..0, missing.into_iter());
envelopes.splice(0..0, resolved.into_iter());

// Consider: reserve + rotate or single splice with chain

7. Documentation Clarity (order.rs:66-67)

The comment "apply missing before resolved" is backwards relative to the code execution order (resolved is actually inserted first at index 0, then missing is inserted before it).


Summary

The implementation provides a solid foundation for envelope ordering with dependency resolution, but needs:

  1. Critical: Loop termination guarantees to prevent infinite loops
  2. High: Thread safety trait bounds for WASM compatibility
  3. Medium: Tests for the core ordering logic
  4. Medium: Better observability for dropped envelopes

The causal sorting logic itself (in causal.rs) is well-tested with property-based tests, which is excellent. The main concerns are in the new async orchestration layer.


  • Read and analyze key implementation files
  • Review code quality and potential bugs
  • Check performance and security considerations
  • Evaluate test coverage concerns
  • Post review feedback

@insipx insipx force-pushed the insipx/causal-sort branch from 4ff01fb to 56b9cb7 Compare November 13, 2025 14:32
@insipx insipx marked this pull request as draft November 13, 2025 14:33
@insipx insipx force-pushed the insipx/causal-sort branch from 56b9cb7 to 9fc52ab Compare November 13, 2025 15:10
@insipx insipx force-pushed the insipx/resolution branch 2 times, most recently from bd2576d to 087a21b Compare November 13, 2025 16:02
@insipx insipx force-pushed the insipx/causal-sort branch from 9e14cc2 to 6db2e33 Compare November 13, 2025 16:05
@insipx insipx force-pushed the insipx/resolution branch 4 times, most recently from 53dc0ca to c9aa03a Compare November 13, 2025 16:35
@codecov
Copy link

codecov bot commented Nov 13, 2025

Codecov Report

❌ Patch coverage is 0% with 28 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.27%. Comparing base (904bfda) to head (21889f8).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
xmtp_api_d14n/src/protocol/order.rs 0.00% 15 Missing ⚠️
xmtp_api_d14n/src/protocol/impls/vector_clock.rs 0.00% 11 Missing ⚠️
xmtp_api_d14n/src/protocol/traits.rs 0.00% 1 Missing ⚠️
..._d14n/src/protocol/traits/dependency_resolution.rs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2771      +/-   ##
==========================================
- Coverage   77.30%   77.27%   -0.03%     
==========================================
  Files         375      376       +1     
  Lines       53692    53719      +27     
==========================================
+ Hits        41504    41512       +8     
- Misses      12188    12207      +19     

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

topic_cursor,
} = self;
sort::timestamp(envelopes).sort()?;
while let Some(missing) = sort::causal(envelopes, topic_cursor).sort()? {
Copy link
Contributor

Choose a reason for hiding this comment

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

Possible infinite loop: while let Some(missing) = sort::causal(...).sort()? can stay Some if resolver.resolve(...) returns no new/related envelopes. Consider detecting no progress (e.g., empty resolved, already-present items, or unchanged missing) and break or return an error.

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

👍 Helpful? React to give us feedback.

@insipx insipx force-pushed the insipx/causal-sort branch from 211ce1c to 157a5d8 Compare November 13, 2025 17:15
@insipx insipx force-pushed the insipx/resolution branch 2 times, most recently from d2d3986 to 4401db3 Compare November 13, 2025 19:40
@insipx insipx force-pushed the insipx/causal-sort branch from 157a5d8 to 20f196a Compare November 13, 2025 19:40
@insipx insipx marked this pull request as ready for review November 13, 2025 20:02
@insipx insipx changed the title add resolution strategy Add an ordering extension to a Vec<Envelope> Nov 13, 2025
@insipx insipx force-pushed the insipx/causal-sort branch from 20f196a to 33eaaea Compare November 13, 2025 20:07
@insipx insipx force-pushed the insipx/causal-sort branch from 33eaaea to d93572e Compare November 13, 2025 20:08
@macroscopeapp
Copy link
Contributor

macroscopeapp bot commented Nov 13, 2025

Add asynchronous ordering for Vec<Envelope> and introduce Ordered<T, R>::order() in order.rs

Introduce OrderedEnvelopeCollection with async order() that sorts by timestamp, resolves causal gaps via resolver.resolve, and iterates until no missing; add VectorClock::missing, rename resolution to resolve, and switch ResolveDependencies::resolve to return ResolutionError.

📍Where to Start

Start with the Ordered<T, R>::order() implementation in order.rs.


Macroscope summarized 21889f8.

@insipx insipx force-pushed the insipx/causal-sort branch from d93572e to 0999e8f Compare November 13, 2025 20:10
@insipx insipx force-pushed the insipx/resolution branch 2 times, most recently from 585234a to f0eef63 Compare November 13, 2025 20:19
@insipx insipx force-pushed the insipx/causal-sort branch from 0999e8f to e00397f Compare November 13, 2025 20:19
@insipx insipx force-pushed the insipx/causal-sort branch 2 times, most recently from 9385776 to 96d5df8 Compare November 13, 2025 21:13
@insipx insipx force-pushed the insipx/causal-sort branch from 96d5df8 to de38fb0 Compare November 13, 2025 21:37
Base automatically changed from insipx/causal-sort to main November 13, 2025 22:19
@insipx insipx enabled auto-merge (squash) November 13, 2025 22:24
@insipx insipx disabled auto-merge November 13, 2025 22:26
Comment on lines +68 to +69
envelopes.splice(0..0, missing.into_iter());
envelopes.splice(0..0, resolved.into_iter());
Copy link
Contributor

Choose a reason for hiding this comment

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

A VecDeque would be more efficient than calling splice.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

using a VecDeque would be better here. there's also a possibility of using reserve+rotate to keep the Vec. I just gave it a shot, but b/c i coupled the implementation of all the sorts to a Vec it required a bit more re-arranging than I would like

I'm going to be opening PRs for testing each these parts, so I'll revisit the VecDeque in those. I'm also anxious to change to the alternative reserve + rotate w/o good unit tests here as well.

@insipx insipx merged commit f77c10d into main Nov 14, 2025
21 of 22 checks passed
@insipx insipx deleted the insipx/resolution branch November 14, 2025 14:38
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