Retry joinRoom up to 5 times in the case of a 504 GATEWAY TIMEOUT

This commit is contained in:
Michael Telatynski 2020-09-14 15:16:29 +01:00
parent c68a980c59
commit 229967aa33
2 changed files with 37 additions and 6 deletions

View File

@ -18,6 +18,7 @@ limitations under the License.
import React from "react"; import React from "react";
import {Store} from 'flux/utils'; import {Store} from 'flux/utils';
import {MatrixError} from "matrix-js-sdk/src/http-api";
import dis from '../dispatcher/dispatcher'; import dis from '../dispatcher/dispatcher';
import {MatrixClientPeg} from '../MatrixClientPeg'; import {MatrixClientPeg} from '../MatrixClientPeg';
@ -26,6 +27,9 @@ import Modal from '../Modal';
import { _t } from '../languageHandler'; import { _t } from '../languageHandler';
import { getCachedRoomIDForAlias, storeRoomAliasInCache } from '../RoomAliasCache'; import { getCachedRoomIDForAlias, storeRoomAliasInCache } from '../RoomAliasCache';
import {ActionPayload} from "../dispatcher/payloads"; import {ActionPayload} from "../dispatcher/payloads";
import {retry} from "../utils/promise";
const NUM_JOIN_RETRY = 5;
const INITIAL_STATE = { const INITIAL_STATE = {
// Whether we're joining the currently viewed room (see isJoining()) // Whether we're joining the currently viewed room (see isJoining())
@ -259,24 +263,32 @@ class RoomViewStore extends Store<ActionPayload> {
}); });
} }
private joinRoom(payload: ActionPayload) { private async joinRoom(payload: ActionPayload) {
this.setState({ this.setState({
joining: true, joining: true,
}); });
MatrixClientPeg.get().joinRoom(
this.state.roomAlias || this.state.roomId, payload.opts, const cli = MatrixClientPeg.get();
).then(() => { const address = this.state.roomAlias || this.state.roomId;
try {
await retry<void, MatrixError>(() => cli.joinRoom(address, payload.opts), NUM_JOIN_RETRY, (err) => {
// if we received a Gateway timeout then retry
return err.httpStatus === 504;
});
// We do *not* clear the 'joining' flag because the Room object and/or our 'joined' member event may not // We do *not* clear the 'joining' flag because the Room object and/or our 'joined' member event may not
// have come down the sync stream yet, and that's the point at which we'd consider the user joined to the // have come down the sync stream yet, and that's the point at which we'd consider the user joined to the
// room. // room.
dis.dispatch({ action: 'join_room_ready' }); dis.dispatch({ action: 'join_room_ready' });
}, (err) => { } catch (err) {
dis.dispatch({ dis.dispatch({
action: 'join_room_error', action: 'join_room_error',
err: err, err: err,
}); });
let msg = err.message ? err.message : JSON.stringify(err); let msg = err.message ? err.message : JSON.stringify(err);
console.log("Failed to join room:", msg); console.log("Failed to join room:", msg);
if (err.name === "ConnectionError") { if (err.name === "ConnectionError") {
msg = _t("There was an error joining the room"); msg = _t("There was an error joining the room");
} else if (err.errcode === 'M_INCOMPATIBLE_ROOM_VERSION') { } else if (err.errcode === 'M_INCOMPATIBLE_ROOM_VERSION') {
@ -296,12 +308,13 @@ class RoomViewStore extends Store<ActionPayload> {
} }
} }
} }
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to join room', '', ErrorDialog, { Modal.createTrackedDialog('Failed to join room', '', ErrorDialog, {
title: _t("Failed to join room"), title: _t("Failed to join room"),
description: msg, description: msg,
}); });
}); }
} }
private getInvitingUserId(roomId: string): string { private getInvitingUserId(roomId: string): string {

View File

@ -68,3 +68,21 @@ export function allSettled<T>(promises: Promise<T>[]): Promise<Array<ISettledFul
})); }));
})); }));
} }
// Helper method to retry a Promise a given number of times or until a predicate fails
export async function retry<T, E extends Error>(fn: () => Promise<T>, num: number, predicate?: (e: E) => boolean) {
let lastErr: E;
for (let i = 0; i < num; i++) {
try {
const v = await fn();
// If `await fn()` throws then we won't reach here
return v;
} catch (err) {
if (predicate && !predicate(err)) {
throw err;
}
lastErr = err;
}
}
throw lastErr;
}