Skip to content

Commit 931cd97

Browse files
authored
Wrap region fetching in a lock (#1711)
1 parent bb6911d commit 931cd97

File tree

1 file changed

+39
-31
lines changed

1 file changed

+39
-31
lines changed

src/room/RegionUrlProvider.ts

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Mutex } from '@livekit/mutex';
12
import type { RegionInfo, RegionSettings } from '@livekit/protocol';
23
import log from '../logger';
34
import { ConnectionError, ConnectionErrorReason } from './errors';
@@ -16,14 +17,47 @@ export class RegionUrlProvider {
1617

1718
private static settingsTimeouts: Map<string, ReturnType<typeof setTimeout>> = new Map();
1819

19-
private static scheduleRefetch(url: URL, token: string, maxAgeInMs: number) {
20-
const timeout = RegionUrlProvider.settingsTimeouts.get(url.host);
20+
private static fetchLock = new Mutex();
21+
22+
private static async fetchRegionSettings(
23+
serverUrl: URL,
24+
token: string,
25+
signal?: AbortSignal,
26+
): Promise<CachedRegionSettings> {
27+
const unlock = await RegionUrlProvider.fetchLock.lock();
28+
try {
29+
const regionSettingsResponse = await fetch(`${getCloudConfigUrl(serverUrl)}/regions`, {
30+
headers: { authorization: `Bearer ${token}` },
31+
signal,
32+
});
33+
if (regionSettingsResponse.ok) {
34+
const maxAge = extractMaxAgeFromRequestHeaders(regionSettingsResponse.headers);
35+
const maxAgeInMs = maxAge ? maxAge * 1000 : DEFAULT_MAX_AGE_MS;
36+
37+
const regionSettings = (await regionSettingsResponse.json()) as RegionSettings;
38+
return { regionSettings, updatedAtInMs: Date.now(), maxAgeInMs };
39+
} else {
40+
throw new ConnectionError(
41+
`Could not fetch region settings: ${regionSettingsResponse.statusText}`,
42+
regionSettingsResponse.status === 401
43+
? ConnectionErrorReason.NotAllowed
44+
: ConnectionErrorReason.InternalError,
45+
regionSettingsResponse.status,
46+
);
47+
}
48+
} finally {
49+
unlock();
50+
}
51+
}
52+
53+
private static async scheduleRefetch(url: URL, token: string, maxAgeInMs: number) {
54+
const timeout = RegionUrlProvider.settingsTimeouts.get(url.hostname);
2155
clearTimeout(timeout);
2256
RegionUrlProvider.settingsTimeouts.set(
23-
url.host,
57+
url.hostname,
2458
setTimeout(async () => {
2559
try {
26-
const newSettings = await fetchRegionSettings(url, token);
60+
const newSettings = await RegionUrlProvider.fetchRegionSettings(url, token);
2761
RegionUrlProvider.updateCachedRegionSettings(url, token, newSettings);
2862
} catch (error: unknown) {
2963
log.debug('auto refetching of region settings failed', { error });
@@ -68,7 +102,7 @@ export class RegionUrlProvider {
68102

69103
/** @internal */
70104
async fetchRegionSettings(abortSignal?: AbortSignal): Promise<CachedRegionSettings> {
71-
return fetchRegionSettings(this.serverUrl, this.token, abortSignal);
105+
return RegionUrlProvider.fetchRegionSettings(this.serverUrl, this.token, abortSignal);
72106
}
73107

74108
async getNextBestRegionUrl(abortSignal?: AbortSignal) {
@@ -108,29 +142,3 @@ export class RegionUrlProvider {
108142
function getCloudConfigUrl(serverUrl: URL) {
109143
return `${serverUrl.protocol.replace('ws', 'http')}//${serverUrl.host}/settings`;
110144
}
111-
112-
async function fetchRegionSettings(
113-
serverUrl: URL,
114-
token: string,
115-
signal?: AbortSignal,
116-
): Promise<CachedRegionSettings> {
117-
const regionSettingsResponse = await fetch(`${getCloudConfigUrl(serverUrl)}/regions`, {
118-
headers: { authorization: `Bearer ${token}` },
119-
signal,
120-
});
121-
if (regionSettingsResponse.ok) {
122-
const maxAge = extractMaxAgeFromRequestHeaders(regionSettingsResponse.headers);
123-
const maxAgeInMs = maxAge ? maxAge * 1000 : DEFAULT_MAX_AGE_MS;
124-
125-
const regionSettings = (await regionSettingsResponse.json()) as RegionSettings;
126-
return { regionSettings, updatedAtInMs: Date.now(), maxAgeInMs };
127-
} else {
128-
throw new ConnectionError(
129-
`Could not fetch region settings: ${regionSettingsResponse.statusText}`,
130-
regionSettingsResponse.status === 401
131-
? ConnectionErrorReason.NotAllowed
132-
: ConnectionErrorReason.InternalError,
133-
regionSettingsResponse.status,
134-
);
135-
}
136-
}

0 commit comments

Comments
 (0)