Skip to content

Commit e1cc26b

Browse files
committed
MatrixRTCSession: handle rate limit errors
1 parent ebe75fe commit e1cc26b

File tree

1 file changed

+37
-14
lines changed

1 file changed

+37
-14
lines changed

src/matrixrtc/MatrixRTCSession.ts

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import { randomString, secureRandomBase64Url } from "../randomstring.ts";
3434
import { EncryptionKeysEventContent } from "./types.ts";
3535
import { decodeBase64, encodeUnpaddedBase64 } from "../base64.ts";
3636
import { KnownMembership } from "../@types/membership.ts";
37-
import { MatrixError, safeGetRetryAfterMs } from "../http-api/errors.ts";
37+
import { HTTPError, MatrixError, safeGetRetryAfterMs } from "../http-api/errors.ts";
3838
import { MatrixEvent } from "../models/event.ts";
3939
import { isLivekitFocusActive } from "./LivekitFocus.ts";
4040
import { ExperimentalGroupCallRoomMemberState } from "../webrtc/groupCall.ts";
@@ -1031,18 +1031,17 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
10311031
const prepareDelayedDisconnection = async (): Promise<void> => {
10321032
try {
10331033
// TODO: If delayed event times out, re-join!
1034-
const res = await this.client._unstable_sendDelayedStateEvent(
1034+
const res = await resendIfRateLimited(() => this.client._unstable_sendDelayedStateEvent(
10351035
this.room.roomId,
10361036
{
10371037
delay: 8000,
10381038
},
10391039
EventType.GroupCallMemberPrefix,
10401040
{}, // leave event
10411041
stateKey,
1042-
);
1042+
));
10431043
this.disconnectDelayId = res.delay_id;
10441044
} catch (e) {
1045-
// TODO: Retry if rate-limited
10461045
logger.error("Failed to prepare delayed disconnection event:", e);
10471046
}
10481047
};
@@ -1058,12 +1057,12 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
10581057
// TODO: Remove this once MSC4140 is stable & doesn't cancel own delayed state
10591058
if (this.disconnectDelayId !== undefined) {
10601059
try {
1061-
await this.client._unstable_updateDelayedEvent(
1062-
this.disconnectDelayId,
1060+
const knownDisconnectDelayId = this.disconnectDelayId;
1061+
await resendIfRateLimited(() => this.client._unstable_updateDelayedEvent(
1062+
knownDisconnectDelayId,
10631063
UpdateDelayedEventAction.Restart,
1064-
);
1064+
));
10651065
} catch (e) {
1066-
// TODO: Make embedded client include errcode, and retry only if not M_NOT_FOUND (or rate-limited)
10671066
logger.warn("Failed to update delayed disconnection event, prepare it again:", e);
10681067
this.disconnectDelayId = undefined;
10691068
await prepareDelayedDisconnection();
@@ -1076,13 +1075,13 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
10761075
let sentDelayedDisconnect = false;
10771076
if (this.disconnectDelayId !== undefined) {
10781077
try {
1079-
await this.client._unstable_updateDelayedEvent(
1080-
this.disconnectDelayId,
1078+
const knownDisconnectDelayId = this.disconnectDelayId;
1079+
await resendIfRateLimited(() => this.client._unstable_updateDelayedEvent(
1080+
knownDisconnectDelayId,
10811081
UpdateDelayedEventAction.Send,
1082-
);
1082+
));
10831083
sentDelayedDisconnect = true;
10841084
} catch (e) {
1085-
// TODO: Retry if rate-limited
10861085
logger.error("Failed to send our delayed disconnection event:", e);
10871086
}
10881087
this.disconnectDelayId = undefined;
@@ -1111,10 +1110,10 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
11111110

11121111
private readonly delayDisconnection = async (): Promise<void> => {
11131112
try {
1114-
await this.client._unstable_updateDelayedEvent(this.disconnectDelayId!, UpdateDelayedEventAction.Restart);
1113+
const knownDisconnectDelayId = this.disconnectDelayId!;
1114+
await resendIfRateLimited(() => this.client._unstable_updateDelayedEvent(knownDisconnectDelayId, UpdateDelayedEventAction.Restart));
11151115
this.scheduleDelayDisconnection();
11161116
} catch (e) {
1117-
// TODO: Retry if rate-limited
11181117
logger.error("Failed to delay our disconnection event:", e);
11191118
}
11201119
};
@@ -1162,3 +1161,27 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
11621161
this.sendEncryptionKeysEvent(newKeyIndex);
11631162
};
11641163
}
1164+
1165+
async function resendIfRateLimited<T>(func: () => Promise<T>, numRetriesAllowed: number = 1): Promise<T> {
1166+
while (true) {
1167+
try {
1168+
return await func();
1169+
} catch (e) {
1170+
if (numRetriesAllowed > 0 && e instanceof HTTPError && e.isRateLimitError()) {
1171+
numRetriesAllowed--;
1172+
let resendDelay: number;
1173+
const defaultMs = 5000;
1174+
try {
1175+
resendDelay = e.getRetryAfterMs() ?? defaultMs;
1176+
logger.info(`Rate limited by server, retrying in ${resendDelay}ms`);
1177+
} catch (e) {
1178+
logger.warn(`Error while retrieving a rate-limit retry delay, retrying after default delay of ${defaultMs}`, e);
1179+
resendDelay = defaultMs;
1180+
}
1181+
await sleep(resendDelay);
1182+
} else {
1183+
throw e;
1184+
}
1185+
}
1186+
};
1187+
}

0 commit comments

Comments
 (0)