Skip to content

fix(core): defer MLS pending-proposals rehydration until LIVE [WPB-24348]#21265

Open
thisisamir98 wants to merge 6 commits intodevfrom
fix/mls-defer-pending-proposals-rehydration-until-catchup-done
Open

fix(core): defer MLS pending-proposals rehydration until LIVE [WPB-24348]#21265
thisisamir98 wants to merge 6 commits intodevfrom
fix/mls-defer-pending-proposals-rehydration-until-catchup-done

Conversation

@thisisamir98
Copy link
Copy Markdown
Collaborator

@thisisamir98 thisisamir98 commented May 8, 2026

SpikeWPB-24348 [Web] Investigate and reduce MLS decryption failures for MlsErrorOther (top decryption error type)

Stop calling initialisePendingProposalsTasks() from initClient. Persisted TaskScheduler tasks often have firing dates in the past, so they run on the next tick while the client is still draining the notification catch-up. That can advance the local MLS epoch ahead of queued application traffic and surface Wrong Epoch / MessageEpochTooOld during decryption.

Rehydrate those timers only when we transition to ConnectionState.LIVE: before resumeProposalProcessing in handleSynchronizationNotification (async marker path) and in the legacy notification stream processor (same ordering).

Add regression tests that drive createLegacyNotificationStreamProcessor directly: one checks that rehydration runs once and before LIVE is emitted; the other checks it is skipped when MLS is disabled.

Stop calling initialisePendingProposalsTasks() from initClient. Persisted
TaskScheduler tasks often have firing dates in the past, so they run on
the next tick while the client is still draining the notification catch-up.
That can advance the local MLS epoch ahead of queued application traffic and
surface Wrong Epoch / MessageEpochTooOld during decryption.

Rehydrate those timers only when we transition to ConnectionState.LIVE:
before resumeProposalProcessing in handleSynchronizationNotification (async
marker path) and in the legacy notification stream processor (same ordering).

Add regression tests that drive createLegacyNotificationStreamProcessor
directly: one checks that rehydration runs once and before LIVE is emitted;
the other checks it is skipped when MLS is disabled.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 8, 2026

🔗 Download Full Report Artifact

🧪 Playwright Test Summary

  • Passed: 15
  • Failed: 0
  • Skipped: 0
  • 🔁 Flaky: 0
  • 📊 Total: 15
  • Total Runtime: 118.2s (~ 1 min 58 sec)

Comment thread libraries/core/src/account.ts Outdated
Comment thread libraries/core/src/account.ts Outdated
Comment thread libraries/core/src/account.test.ts Outdated
*/
private readonly rehydrateMlsPendingProposalsTasksOnLiveTransition = async (): Promise<void> => {
if (this.hasMLSDevice && this.service?.mls) {
await this.service.mls.initialisePendingProposalsTasks();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What is the failure policy if initialisePendingProposalsTasks() rejects here? A rejection can block the hand-off path

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

errors are absorbed inside initialisePendingProposalsTasks, so this line should not reject.
see: https://github.com/wireapp/wire-webapp/blob/dev/libraries/core/src/messagingProtocols/mls/mlsService/mlsService.ts#L1133-L1162

* if the marker ID matches the current marker ID.
*/
if (markerId === currentMarkerId) {
await this.rehydrateMlsPendingProposalsTasksOnLiveTransition();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can this path run multiple times across reconnect/LIVE transitions? If yes, please confirm rehydration is idempotent (or guard it) to avoid duplicate pending-proposal timers.

void this.notificationProcessingQueue
.push(async () => {
this.logger.info(`Resuming message sending. ${getQueueLength()} messages to be sent`);
await this.rehydrateMlsPendingProposalsTasksOnLiveTransition();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same idempotency concern as line 976

thisisamir98 and others added 3 commits May 11, 2026 12:01
Co-authored-by: Christian Rackerseder <git@echooff.de>
…up-done' of github.com:wireapp/wire-webapp into fix/mls-defer-pending-proposals-rehydration-until-catchup-done
@sonarqubecloud
Copy link
Copy Markdown

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.

3 participants