Skip to content

Commit

Permalink
Add a timeout system to avoid this function causing messages to queue up
Browse files Browse the repository at this point in the history
  • Loading branch information
Half-Shot committed May 6, 2021
1 parent 5628335 commit 682cd97
Showing 1 changed file with 33 additions and 9 deletions.
42 changes: 33 additions & 9 deletions src/bridge/RoomConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,52 @@ interface RoomConfigContent {
}

const MAX_CACHE_SIZE = 512;
const STATE_TIMEOUT_MS = 2000;

const log = getLogger("RoomConfig");
export class RoomConfig {
public static readonly STATE_EVENT_TYPE = 'org.matrix.appservice-irc.room-config';
public static readonly STATE_EVENT_TYPE = 'org.matrix.appservice-irc.config';
private cache = new QuickLRU<string, RoomConfigContent|undefined>({maxSize: MAX_CACHE_SIZE});
constructor(private bridge: Bridge) { }

/**
* Fetch the state for the room, preferring a keyed state event over a global one.
* This request will time out after `STATE_TIMEOUT_MS` if the state could not be fetched in time.
* @param roomId The Matrix room ID
* @param ircRoom The IRC room we want the configuration for.
* @returns A content object containing the configuration, or null if the event was not found or the
* request timed out.
*/
private async getRoomState(roomId: string, ircRoom?: IrcRoom): Promise<RoomConfigContent|null> {
const cacheKey = `${roomId}:${ircRoom?.getId() || 'global'}`;
let keyedConfig = this.cache.get(cacheKey);
if (keyedConfig) {
return keyedConfig;
}
const intent = this.bridge.getIntent();
keyedConfig = ircRoom && await intent.getStateEvent(roomId, RoomConfig.STATE_EVENT_TYPE, ircRoom.getId(), true);
if (!keyedConfig) {
// Fall back to an empty key
keyedConfig = await intent.getStateEvent(roomId, RoomConfig.STATE_EVENT_TYPE, '', true);
const internalFunc = async () => {
const intent = this.bridge.getIntent();
keyedConfig = ircRoom &&
await intent.getStateEvent(roomId, RoomConfig.STATE_EVENT_TYPE, ircRoom.getId(), true);
if (!keyedConfig) {
// Fall back to an empty key
keyedConfig = await intent.getStateEvent(roomId, RoomConfig.STATE_EVENT_TYPE, '', true);
}
log.debug(
`Stored new config for ${cacheKey}: ${keyedConfig ? 'No config set' : JSON.stringify(keyedConfig)}`
);
this.cache.set(cacheKey, keyedConfig || undefined);
return keyedConfig as RoomConfigContent|null;
}
log.debug(`Stored new config for ${cacheKey}:`, keyedConfig || 'No config set');
this.cache.set(cacheKey, keyedConfig || undefined);
return keyedConfig as RoomConfigContent|null;
// We don't want to spend too long trying to fetch the state, so return null.
return Promise.race([
internalFunc(),
new Promise<null>(res => setTimeout(() => res(null), STATE_TIMEOUT_MS)),
// We *never* want this function to throw, as it's critical for the bridging of messages.
// Instead we return null for any errors.
]).catch(ex => {
log.warn(`Failed to fetch state for ${cacheKey}`, ex);
return null;
})
}

/**
Expand Down

0 comments on commit 682cd97

Please sign in to comment.