@@ -73,29 +73,26 @@ export interface IMembershipManager {
7373}
7474
7575/* MembershipActionTypes:
76- ▼
77- ┌─────────────────────┐
78- │SendFirstDelayedEvent│
79- └─────────────────────┘
80- │
81- ▼
82- ┌─────────────┐
83- ┌────────────│SendJoinEvent│────────────┐
84- │ └─────────────┘ │
85- │ ┌─────┐ ┌──────┐ │ ┌──────┐
86- ▼ ▼ │ │ ▼ ▼ ▼ │
76+
77+ On Join: ───────────────┐ ┌───────────────(1)───────────┐
78+ ▼ ▼ │
79+ ┌────────────────┐ │
80+ │SendDelayedEvent│ ──────(2)───┐ │
81+ └────────────────┘ │ │
82+ │(3) │ │
83+ ▼ │ │
84+ ┌─────────────┐ │ │
85+ ┌──────(4)───│SendJoinEvent│────(4)─────┐ │ │
86+ │ └─────────────┘ │ │ │
87+ │ ┌─────┐ ┌──────┐ │ │ │
88+ ▼ ▼ │ │ ▼ ▼ ▼ │
8789┌────────────┐ │ │ ┌───────────────────┐ │
8890│UpdateExpiry│ │ │ │RestartDelayedEvent│ │
8991└────────────┘ │ │ └───────────────────┘ │
90- │ │ │ │ │ │
91- └─────┘ └──────┘ │ │
92- │ │
93- ┌────────────────────┐ │ │
94- │SendMainDelayedEvent│◄───────┘ │
95- └───────────────────┬┘ │
96- │ │
97- └─────────────────────┘
98- STOP ALL ABOVE
92+ │ │ │ │ │ │
93+ └─────┘ └──────┘ └───────┘
94+
95+ On Leave: ───────── STOP ALL ABOVE
9996 ▼
10097 ┌───────────────────────────────┐
10198 │ SendScheduledDelayedLeaveEvent│
@@ -105,29 +102,35 @@ export interface IMembershipManager {
105102 ┌──────────────┐
106103 │SendLeaveEvent│
107104 └──────────────┘
108-
105+ (1) [Not found error] results in resending the delayed event
106+ (2) [hasMemberEvent = true] Sending the delayed event if we
107+ already have a call member event results jumping to the
108+ RestartDelayedEvent loop directly
109+ (3) [hasMemberEvent = false] if there is not call member event
110+ sending it is the next step
111+ (4) Both (UpdateExpiry and RestartDelayedEvent) actions are
112+ scheduled when successfully sending the state event
109113*/
114+
110115/**
111116 * The different types of actions the MembershipManager can take.
112117 * @internal
113118 */
114119export enum MembershipActionType {
115- SendFirstDelayedEvent = "SendFirstDelayedEvent " ,
120+ SendDelayedEvent = "SendDelayedEvent " ,
116121 // -> MembershipActionType.SendJoinEvent if successful
117- // -> DelayedLeaveActionType.SendFirstDelayedEvent on error, retry sending the first delayed event.
122+ // -> DelayedLeaveActionType.SendDelayedEvent on error, retry sending the first delayed event.
123+ // -> DelayedLeaveActionType.RestartDelayedEvent on success start updating the delayed event
118124 SendJoinEvent = "SendJoinEvent" ,
119125 // -> MembershipActionType.SendJoinEvent if we run into a rate limit and need to retry
120126 // -> MembershipActionType.Update if we successfully send the join event then schedule the expire event update
121127 // -> DelayedLeaveActionType.RestartDelayedEvent to recheck the delayed event
122128 RestartDelayedEvent = "RestartDelayedEvent" ,
123129 // -> DelayedLeaveActionType.SendMainDelayedEvent on missing delay id but there is a rtc state event
124- // -> DelayedLeaveActionType.SendFirstDelayedEvent on missing delay id and there is no state event
130+ // -> DelayedLeaveActionType.SendDelayedEvent on missing delay id and there is no state event
125131 // -> DelayedLeaveActionType.RestartDelayedEvent on success we schedule the next restart
126132 UpdateExpiry = "UpdateExpiry" ,
127133 // -> MembershipActionType.Update if the timeout has passed so the next update is required.
128- SendMainDelayedEvent = "SendMainDelayedEvent" ,
129- // -> DelayedLeaveActionType.RestartDelayedEvent on success start updating the delayed event
130- // -> DelayedLeaveActionType.SendMainDelayedEvent on error try again
131134 SendScheduledDelayedLeaveEvent = "SendScheduledDelayedLeaveEvent" ,
132135 // -> MembershipActionType.SendLeaveEvent on failiour (not found) we need to send the leave manually and cannot use the scheduled delayed event
133136 // -> DelayedLeaveActionType.SendScheduledDelayedLeaveEvent on error we try again.
@@ -256,7 +259,7 @@ export class MembershipManager implements IMembershipManager {
256259 // If one of these actions are scheduled or are getting inserted in the next iteration, we should already
257260 // take care of our missing membership.
258261 const sendingMembershipActions = [
259- MembershipActionType . SendFirstDelayedEvent ,
262+ MembershipActionType . SendDelayedEvent ,
260263 MembershipActionType . SendJoinEvent ,
261264 ] ;
262265 logger . warn ( "Missing own membership: force re-join" ) ;
@@ -393,10 +396,10 @@ export class MembershipManager implements IMembershipManager {
393396 private async membershipLoopHandler ( type : MembershipActionType ) : Promise < ActionUpdate > {
394397 this . oldStatus = this . status ;
395398 switch ( type ) {
396- case MembershipActionType . SendFirstDelayedEvent : {
399+ case MembershipActionType . SendDelayedEvent : {
397400 // Before we start we check if we come from a state where we have a delay id.
398401 if ( ! this . state . delayId ) {
399- return this . sendFirstDelayedLeaveEvent ( ) ; // Normal case without any previous delayed id.
402+ this . sendOrResendDelayedLeaveEvent ( ) ; // Normal case without any previous delayed id.
400403 } else {
401404 // This can happen if someone else (or another client) removes our own membership event.
402405 // It will trigger `onRTCSessionMemberUpdate` queue `MembershipActionType.SendFirstDelayedEvent`.
@@ -411,17 +414,10 @@ export class MembershipManager implements IMembershipManager {
411414 case MembershipActionType . RestartDelayedEvent : {
412415 if ( ! this . state . delayId ) {
413416 // Delay id got reset. This action was used to check if the hs canceled the delayed event when the join state got sent.
414- return createInsertActionUpdate (
415- this . state . hasMemberStateEvent
416- ? MembershipActionType . SendMainDelayedEvent
417- : MembershipActionType . SendFirstDelayedEvent ,
418- ) ;
417+ return createInsertActionUpdate ( MembershipActionType . SendDelayedEvent ) ;
419418 }
420419 return this . restartDelayedEvent ( this . state . delayId ) ;
421420 }
422- case MembershipActionType . SendMainDelayedEvent : {
423- return this . sendMainDelayedEvent ( ) ;
424- }
425421 case MembershipActionType . SendScheduledDelayedLeaveEvent : {
426422 // We are already good
427423 if ( ! this . state . hasMemberStateEvent ) {
@@ -452,7 +448,11 @@ export class MembershipManager implements IMembershipManager {
452448 }
453449
454450 // HANDLERS (used in the membershipLoopHandler)
455- private async sendFirstDelayedLeaveEvent ( ) : Promise < ActionUpdate > {
451+ private async sendOrResendDelayedLeaveEvent ( ) : Promise < ActionUpdate > {
452+ // We can reach this at the start of a call (where we do not yet have a membership: state.hasMemberStateEvent=false)
453+ // or during a call if the state event canceled our delayed event or caused by an unexpected error that removed our delayed event.
454+ // (Another client could have canceled it, the homeserver might have removed/lost it due to a restart, ...)
455+ // In the `then` and `catch` block we treat both cases differently. "if (this.state.hasMemberStateEvent) {} else {}"
456456 return await this . client
457457 . _unstable_sendDelayedStateEvent (
458458 this . room . roomId ,
@@ -465,27 +465,44 @@ export class MembershipManager implements IMembershipManager {
465465 )
466466 . then ( ( response ) => {
467467 // On success we reset retries and set delayId.
468- this . state . rateLimitRetries . set ( MembershipActionType . SendFirstDelayedEvent , 0 ) ;
469- this . state . networkErrorRetries . set ( MembershipActionType . SendFirstDelayedEvent , 0 ) ;
468+ this . resetRateLimitCounter ( MembershipActionType . SendDelayedEvent ) ;
470469 this . state . delayId = response . delay_id ;
471- return createInsertActionUpdate ( MembershipActionType . SendJoinEvent ) ;
470+ if ( this . state . hasMemberStateEvent ) {
471+ // Delayed event got send because it got lost due to state event auto cancel
472+ return createInsertActionUpdate (
473+ MembershipActionType . RestartDelayedEvent ,
474+ this . membershipKeepAlivePeriod ,
475+ ) ;
476+ } else {
477+ // Delayed event got send because we just joined
478+ return createInsertActionUpdate ( MembershipActionType . SendJoinEvent ) ;
479+ }
472480 } )
473481 . catch ( ( e ) => {
474- const repeatActionType = MembershipActionType . SendFirstDelayedEvent ;
482+ const repeatActionType = MembershipActionType . SendDelayedEvent ;
475483 if ( this . manageMaxDelayExceededSituation ( e ) ) {
476484 return createInsertActionUpdate ( repeatActionType ) ;
477485 }
478486 const update = this . actionUpdateFromErrors ( e , repeatActionType , "sendDelayedStateEvent" ) ;
479487 if ( update ) return update ;
480488
481- // log and fall through
482- if ( this . isUnsupportedDelayedEndpoint ( e ) ) {
483- logger . info ( "Not using delayed event because the endpoint is not supported" ) ;
489+ if ( this . state . hasMemberStateEvent ) {
490+ // Delayed event got send because it got lost due to state event auto cancel
491+
492+ // Don't do any other delayed event work if its not supported.
493+ if ( this . isUnsupportedDelayedEndpoint ( e ) ) return { } ;
494+ throw Error ( "Could not send delayed event, even though delayed events are supported. " + e ) ;
484495 } else {
485- logger . info ( "Not using delayed event because: " + e ) ;
496+ // Delayed event got send because we just joined
497+ // log and fall through
498+ if ( this . isUnsupportedDelayedEndpoint ( e ) ) {
499+ logger . info ( "Not using delayed event because the endpoint is not supported" ) ;
500+ } else {
501+ logger . info ( "Not using delayed event because: " + e ) ;
502+ }
503+ // On any other error we fall back to not using delayed events and send the join state event immediately
504+ return createInsertActionUpdate ( MembershipActionType . SendJoinEvent ) ;
486505 }
487- // On any other error we fall back to not using delayed events and send the join state event immediately
488- return createInsertActionUpdate ( MembershipActionType . SendJoinEvent ) ;
489506 } ) ;
490507 }
491508
@@ -495,11 +512,11 @@ export class MembershipManager implements IMembershipManager {
495512 . _unstable_updateDelayedEvent ( delayId , UpdateDelayedEventAction . Cancel )
496513 . then ( ( ) => {
497514 this . state . delayId = undefined ;
498- this . resetRateLimitCounter ( MembershipActionType . SendFirstDelayedEvent ) ;
499- return createReplaceActionUpdate ( MembershipActionType . SendFirstDelayedEvent ) ;
515+ this . resetRateLimitCounter ( MembershipActionType . SendDelayedEvent ) ;
516+ return createReplaceActionUpdate ( MembershipActionType . SendDelayedEvent ) ;
500517 } )
501518 . catch ( ( e ) => {
502- const repeatActionType = MembershipActionType . SendFirstDelayedEvent ;
519+ const repeatActionType = MembershipActionType . SendDelayedEvent ;
503520 const update = this . actionUpdateFromErrors ( e , repeatActionType , "updateDelayedEvent" ) ;
504521 if ( update ) return update ;
505522
@@ -538,7 +555,7 @@ export class MembershipManager implements IMembershipManager {
538555 const repeatActionType = MembershipActionType . RestartDelayedEvent ;
539556 if ( this . isNotFoundError ( e ) ) {
540557 this . state . delayId = undefined ;
541- return createInsertActionUpdate ( MembershipActionType . SendMainDelayedEvent ) ;
558+ return createInsertActionUpdate ( MembershipActionType . SendDelayedEvent ) ;
542559 }
543560 // If the HS does not support delayed events we wont reschedule.
544561 if ( this . isUnsupportedDelayedEndpoint ( e ) ) return { } ;
@@ -552,40 +569,6 @@ export class MembershipManager implements IMembershipManager {
552569 } ) ;
553570 }
554571
555- private async sendMainDelayedEvent ( ) : Promise < ActionUpdate > {
556- return await this . client
557- . _unstable_sendDelayedStateEvent (
558- this . room . roomId ,
559- {
560- delay : this . membershipServerSideExpiryTimeout ,
561- } ,
562- EventType . GroupCallMemberPrefix ,
563- { } , // leave event
564- this . stateKey ,
565- )
566- . then ( ( response ) => {
567- this . state . delayId = response . delay_id ;
568- this . resetRateLimitCounter ( MembershipActionType . SendMainDelayedEvent ) ;
569- return createInsertActionUpdate (
570- MembershipActionType . RestartDelayedEvent ,
571- this . membershipKeepAlivePeriod ,
572- ) ;
573- } )
574- . catch ( ( e ) => {
575- const repeatActionType = MembershipActionType . SendMainDelayedEvent ;
576- // Don't do any other delayed event work if its not supported.
577- if ( this . isUnsupportedDelayedEndpoint ( e ) ) return { } ;
578-
579- if ( this . manageMaxDelayExceededSituation ( e ) ) {
580- return createInsertActionUpdate ( repeatActionType ) ;
581- }
582- const update = this . actionUpdateFromErrors ( e , repeatActionType , "updateDelayedEvent" ) ;
583- if ( update ) return update ;
584-
585- throw Error ( "Could not send delayed event, even though delayed events are supported. " + e ) ;
586- } ) ;
587- }
588-
589572 private async sendScheduledDelayedLeaveEventOrFallbackToSendLeaveEvent ( delayId : string ) : Promise < ActionUpdate > {
590573 return await this . client
591574 . _unstable_updateDelayedEvent ( delayId , UpdateDelayedEventAction . Send )
@@ -887,9 +870,8 @@ export class MembershipManager implements IMembershipManager {
887870 if ( actions . length === 1 ) {
888871 const { type } = actions [ 0 ] ;
889872 switch ( type ) {
890- case MembershipActionType . SendFirstDelayedEvent :
873+ case MembershipActionType . SendDelayedEvent :
891874 case MembershipActionType . SendJoinEvent :
892- case MembershipActionType . SendMainDelayedEvent :
893875 return Status . Connecting ;
894876 case MembershipActionType . UpdateExpiry : // where no delayed events
895877 return Status . Connected ;
@@ -904,7 +886,7 @@ export class MembershipManager implements IMembershipManager {
904886 // normal state for connected with delayed events
905887 if (
906888 ( types . includes ( MembershipActionType . RestartDelayedEvent ) ||
907- types . includes ( MembershipActionType . SendMainDelayedEvent ) ) &&
889+ ( types . includes ( MembershipActionType . SendDelayedEvent ) && this . state . hasMemberStateEvent ) ) &&
908890 types . includes ( MembershipActionType . UpdateExpiry )
909891 ) {
910892 return Status . Connected ;
0 commit comments