Skip to content

feat(threads): automatically subscribe a user to a thread under the semantics of MSC4306 #5462

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

bnjbvr
Copy link
Member

@bnjbvr bnjbvr commented Jul 29, 2025

Based on #5455 + #5461, this implements automatic subscription to threads which explicitly mention the current user (via an intentional room or user mention), and likewise sends a manual subscription to a thread when replying into it.

There's a single background task, owned by the event cache, that will listen to two input streams, and create automatic thread subscriptions based on these streams.

  • one stream is the global event cache linked chunk update stream, introduced in this PR (and that could be fused in the future with the RoomEventCacheGenericUpdate stream). Based on the new events propagated in a linked chunk update, we can compute the notifications for those, and decide to subscribe to a thread, based on those.
  • the other stream is the global send queue stream (thanks for adding this in another PR!). Ideally, we'd have the full event's content when it's sent, and we could only react to Sent updates. We could embed the sent event in the Sent variant, as a simplification; otherwise, we keep just a bit of state, to link the to-be-sent event (transaction id) to its thread root.

Topics to discuss:

  • having the event cache own this task is a bit strange since it also listens to the send queue. We could put it in the client, but at this point, since both the send q and the event cache should be enabled at the same time, and we'd want to have the event cache stream around, I thought it was just as simple to put it in the event cache.
  • as hinted above: the send q update Sent could embed the sent event, which would avoid the mapping of to-be-sent event to a thread root.

PR is a bit big, but mostly because of the tests; the core code isn't that big.

Part of #4869.

Copy link

codecov bot commented Jul 29, 2025

Codecov Report

❌ Patch coverage is 92.72727% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.59%. Comparing base (247ec1d) to head (a682a46).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
crates/matrix-sdk/src/event_cache/mod.rs 85.18% 4 Missing ⚠️
crates/matrix-sdk/src/event_cache/room/mod.rs 86.66% 0 Missing and 2 partials ⚠️
crates/matrix-sdk-common/src/serde_helpers.rs 85.71% 0 Missing and 1 partial ⚠️
crates/matrix-sdk/src/room/mod.rs 96.55% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5462      +/-   ##
==========================================
+ Coverage   88.56%   88.59%   +0.02%     
==========================================
  Files         340      340              
  Lines       93686    93780      +94     
  Branches    93686    93780      +94     
==========================================
+ Hits        82975    83082     +107     
+ Misses       6578     6565      -13     
  Partials     4133     4133              

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

Copy link

codspeed-hq bot commented Jul 29, 2025

CodSpeed Performance Report

Merging #5462 will not alter performance

Comparing bnjbvr/auto-sub-threads (a682a46) with main (247ec1d)

Summary

✅ 31 untouched benchmarks

@bnjbvr bnjbvr force-pushed the bnjbvr/auto-sub-threads branch 5 times, most recently from e95d796 to 8da211c Compare August 12, 2025 13:14
@bnjbvr bnjbvr marked this pull request as ready for review August 12, 2025 13:14
@bnjbvr bnjbvr requested a review from a team as a code owner August 12, 2025 13:14
@bnjbvr bnjbvr requested review from Hywan and removed request for a team August 12, 2025 13:14
@bnjbvr bnjbvr marked this pull request as draft August 13, 2025 08:16
Copy link
Member

@Hywan Hywan left a comment

Choose a reason for hiding this comment

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

Looks good. I don't understand all the details for the moment. I've left a couple of questions. Thanks for your contributions!

@@ -298,6 +303,8 @@ impl RoomPagination {
});
}

subscribe_to_new_threads(&room, new_thread_subs).await;
Copy link
Member

Choose a reason for hiding this comment

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

Can you add a comment here to explain why we call subscribe_to_new_threads at this place please?

Copy link
Member

Choose a reason for hiding this comment

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

Re-reading this, I don't understand why we subscribe to threads here :-].

Copy link
Member Author

Choose a reason for hiding this comment

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

This is the "main" events timeline, which has threaded and non-threaded events. So when we're back-paginating, we might run into threaded for which no subscriptions have ever been computed (i.e. the threads existed before we created the subscription mechanism). In this case, we still want to be able to "catch up" and automatically subscribe the user.

/// [`push_context_for_threads_subscriptions`] function, available in the same
/// module.
#[derive(Debug, Default)]
pub struct ThreadPushContext(Option<PushContext>);
Copy link
Member

Choose a reason for hiding this comment

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

Why not type ThreadPushContext = Option<PushContext>? What the benefits of having a new type?

Copy link
Member Author

Choose a reason for hiding this comment

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

Newtypes allow hiding the inner workings. I don't want users to pass a general ThreadPushContext to the method that determines whether we should subscribe to a thread or not, because the push context shouldn't have the thread-subscription check. So it has to be a specific flavor of PushContext to be passed in there, and I don't want the user to care about this; they can create a ThreadPushContext with the other function, and not think too much about the implementation details.

@bnjbvr bnjbvr force-pushed the bnjbvr/auto-sub-threads branch from d0f4d67 to 2376763 Compare August 14, 2025 10:50
@bnjbvr bnjbvr force-pushed the bnjbvr/auto-sub-threads branch from d2c99cb to a682a46 Compare August 14, 2025 11:53
@bnjbvr bnjbvr marked this pull request as ready for review August 14, 2025 11:58
@bnjbvr
Copy link
Member Author

bnjbvr commented Aug 14, 2025

Ready for another round of review 🥳 This also implements support for manually subscribing when an event is sent via the send queue, as per the semantics of MSC4306.

PTAL 🥳

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.

2 participants