fix(core): defer MLS pending-proposals rehydration until LIVE [WPB-24348]#21265
fix(core): defer MLS pending-proposals rehydration until LIVE [WPB-24348]#21265thisisamir98 wants to merge 6 commits intodevfrom
Conversation
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.
|
🔗 Download Full Report Artifact 🧪 Playwright Test Summary
|
| */ | ||
| private readonly rehydrateMlsPendingProposalsTasksOnLiveTransition = async (): Promise<void> => { | ||
| if (this.hasMLSDevice && this.service?.mls) { | ||
| await this.service.mls.initialisePendingProposalsTasks(); |
There was a problem hiding this comment.
What is the failure policy if initialisePendingProposalsTasks() rejects here? A rejection can block the hand-off path
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
Same idempotency concern as line 976
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
|



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.