Skip to content

Commit 11e758e

Browse files
committed
Remove redundant sendDelayedEventAction
We do already have the state `hasMemberEvent` that allows to distinguish the two cases. No need to create two dedicated actions.
1 parent f4de883 commit 11e758e

2 files changed

Lines changed: 75 additions & 93 deletions

File tree

src/matrixrtc/NewMembershipManager.ts

Lines changed: 72 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -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
*/
114119
export 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;

src/matrixrtc/NewMembershipManagerActionScheduler.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export class ActionScheduler {
7373
return;
7474
}
7575
this.running = true;
76-
this._actions = [{ ts: Date.now(), type: MembershipActionType.SendFirstDelayedEvent }];
76+
this._actions = [{ ts: Date.now(), type: MembershipActionType.SendDelayedEvent }];
7777
try {
7878
while (this._actions.length > 0) {
7979
// Sort so next (smallest ts) action is at the beginning
@@ -98,7 +98,7 @@ export class ActionScheduler {
9898
`\nDate.now: "${Date.now()}`,
9999
);
100100
try {
101-
// `this.wakeup` can also be called and sets the `wakupUpdate` object while we are in the handler.
101+
// `this.wakeup` can also be called and sets the `wakeupUpdate` object while we are in the handler.
102102
handlerResult = await this.membershipLoopHandler(nextAction.type as MembershipActionType);
103103
} catch (e) {
104104
throw Error(`The MembershipManager shut down because of the end condition: ${e}`);
@@ -125,7 +125,7 @@ export class ActionScheduler {
125125
}
126126

127127
public initiateJoin(): void {
128-
this.wakeup?.({ replace: [{ ts: Date.now(), type: MembershipActionType.SendFirstDelayedEvent }] });
128+
this.wakeup?.({ replace: [{ ts: Date.now(), type: MembershipActionType.SendDelayedEvent }] });
129129
}
130130
public initiateLeave(): void {
131131
this.wakeup?.({ replace: [{ ts: Date.now(), type: MembershipActionType.SendScheduledDelayedLeaveEvent }] });

0 commit comments

Comments
 (0)