Skip to content

Commit f60553a

Browse files
committed
[dashboard] Improve RTT measurement
1 parent 30169f2 commit f60553a

File tree

7 files changed

+32
-39
lines changed

7 files changed

+32
-39
lines changed

components/dashboard/src/Login.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import fresh from "./images/welcome/fresh.svg";
2222
import prebuild from "./images/welcome/prebuild.svg";
2323
import exclamation from "./images/exclamation.svg";
2424
import { getURLHash } from "./App";
25-
import { measureAndPickWorkspaceClusterRegion } from "./start/choose-region";
2625

2726

2827
function Item(props: { icon: string, iconSize?: string, text: string }) {
@@ -85,8 +84,6 @@ export function Login() {
8584
const authorizeSuccessful = async (payload?: string) => {
8685
updateUser().catch(console.error);
8786

88-
await measureAndPickWorkspaceClusterRegion();
89-
9087
// Check for a valid returnTo in payload
9188
const safeReturnTo = getSafeURLRedirect(payload);
9289
if (safeReturnTo) {

components/dashboard/src/start/CreateWorkspace.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { openAuthorizeWindow } from "../provider-utils";
1717
import { SelectAccountPayload } from "@gitpod/gitpod-protocol/lib/auth";
1818
import { SelectAccountModal } from "../settings/SelectAccountModal";
1919
import { watchHeadlessLogs } from "../components/PrebuildLogs";
20+
import { measureAndPickWorkspaceClusterRegion } from "./choose-region";
2021

2122
const WorkspaceLogs = React.lazy(() => import('../components/WorkspaceLogs'));
2223

@@ -46,6 +47,9 @@ export default class CreateWorkspace extends React.Component<CreateWorkspaceProp
4647
// Invalidate any previous result.
4748
this.setState({ result: undefined, stillParsing: true });
4849

50+
// Ensure we'll land on a good workspace custer
51+
await measureAndPickWorkspaceClusterRegion();
52+
4953
// We assume anything longer than 3 seconds is no longer just parsing the context URL (i.e. it's now creating a workspace).
5054
let timeout = setTimeout(() => this.setState({ stillParsing: false }), 3000);
5155

components/dashboard/src/start/StartPage.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
import { useEffect } from 'react';
88
import gitpodIcon from '../icons/gitpod.svg';
9-
import { measureAndPickWorkspaceClusterRegion } from './choose-region';
109

1110
export enum StartPhase {
1211
Checking = 0,
@@ -85,7 +84,6 @@ export function StartPage(props: StartPageProps) {
8584
const { phase, error } = props;
8685
let title = props.title || getPhaseTitle(phase, error);
8786
useEffect(() => { document.title = 'Starting — Gitpod' }, []);
88-
measureAndPickWorkspaceClusterRegion();
8987
return <div className="w-screen h-screen align-middle">
9088
<div className="flex flex-col mx-auto items-center text-center h-screen">
9189
<div className="h-1/3"></div>

components/dashboard/src/start/StartWorkspace.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import PendingChangesDropdown from "../components/PendingChangesDropdown";
1515
import { watchHeadlessLogs } from "../components/PrebuildLogs";
1616
import { getGitpodService, gitpodHostUrl } from "../service/service";
1717
import { StartPage, StartPhase, StartWorkspaceError } from "./StartPage";
18+
import { measureAndPickWorkspaceClusterRegion } from "./choose-region";
1819
const sessionId = v4();
1920

2021
const WorkspaceLogs = React.lazy(() => import('../components/WorkspaceLogs'));
@@ -117,6 +118,9 @@ export default class StartWorkspace extends React.Component<StartWorkspaceProps,
117118
}
118119
}
119120

121+
// Ensure we'll land on a good workspace custer - this call will take max 1 second
122+
await measureAndPickWorkspaceClusterRegion();
123+
120124
const { workspaceId } = this.props;
121125
try {
122126
const result = await getGitpodService().server.startWorkspace(workspaceId, { forceDefaultImage });

components/dashboard/src/start/choose-region.ts

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,49 +14,45 @@ import { getGitpodService } from "../service/service";
1414
* @returns void
1515
*/
1616
async function measureAndPickWorkspaceClusterRegion(): Promise<void> {
17-
const eps = await getGitpodService().server.listWorkspaceClusterRTTEndpoints();
17+
const localStorageKey = "measureAndPickWorkspaceClusterRegion.lastMeasurement";
1818

19-
if (!!eps.lastMeasurement) {
20-
const lrm = Date.parse(eps.lastMeasurement);
21-
if (Date.now() - lrm < 6*60*60*1000) {
22-
// we checked within the last six hours. Nothing to do here.
19+
let lastCheck = localStorage.getItem(localStorageKey);
20+
try {
21+
if (!lastCheck || (Date.now() - Date.parse(lastCheck)) < 6*60*60*1000) {
22+
// we've recently done the check.
2323
return;
2424
}
25+
} catch (err) {
26+
// Date.parse can fail ... in which case we assume we haven't done the RTT measurement recently.
2527
}
2628

29+
const eps = await getGitpodService().server.listWorkspaceClusterRTTEndpoints();
30+
2731
const region = await Promise.race(eps.candidates.map(ep => measureRTT(ep.endpoint, ep.region)));
2832
if (!region) {
2933
console.warn("did not find a prefered workspace cluster region");
3034
return;
3135
}
3236

37+
localStorage.setItem(localStorageKey, new Date().toISOString())
3338
await getGitpodService().server.setWorkspaceClusterPreferences({ region });
3439
}
3540

3641
async function measureRTT(endpoint: string, region: string): Promise<string | undefined> {
37-
const laps = 5;
38-
let count = 0;
39-
for (let i = 0; i < laps; i++) {
40-
const controller = new AbortController();
41-
const abort = setTimeout(() => controller.abort(), 1000);
42-
43-
try {
44-
await fetch(endpoint, {
45-
cache: "no-cache",
46-
signal: controller.signal,
47-
});
48-
count++;
49-
} catch (err) {
50-
console.debug(`failed to fetch RTT endpoint ${endpoint}: ${err}`);
51-
} finally {
52-
clearTimeout(abort);
53-
}
54-
}
55-
56-
if (count < 5) {
57-
// we haven't completed all RTT measurements, hence take a penalty lap.
58-
await new Promise(resolve => setTimeout(resolve, laps * 1000));
59-
return;
42+
const controller = new AbortController();
43+
const abort = setTimeout(() => controller.abort(), 1000);
44+
45+
try {
46+
await Promise.all(Array(5).map(async () => {
47+
try {
48+
await fetch(endpoint, { cache: "no-cache", signal: controller.signal, });
49+
} catch (err) {
50+
// we don't want a single error to abort the race. For example, it's ok
51+
// if the RTT endpoints return 404.
52+
}
53+
}))
54+
} finally {
55+
clearTimeout(abort);
6056
}
6157

6258
return region;

components/gitpod-protocol/src/protocol.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,13 +1254,9 @@ export interface Terms {
12541254

12551255
export interface WorkspaceClusterPreference {
12561256
region?: string;
1257-
// lrm == lastRegionMeasurement. Not writing the full name to save
1258-
// a few 100mb in the database.
1259-
lrm?: string;
12601257
}
12611258

12621259
export interface WorkspaceClusterRTTEndpoints {
1263-
lastMeasurement?: string;
12641260
candidates: {
12651261
region: string;
12661262
endpoint: string;

components/server/src/workspace/gitpod-server-impl.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2196,7 +2196,6 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
21962196
});
21972197

21982198
return {
2199-
lastMeasurement: user.additionalData?.clusterPreferences?.lrm,
22002199
candidates: [...new Set(allEndpoints)]
22012200
};
22022201
}
@@ -2209,7 +2208,6 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
22092208

22102209
// don't just dump `pref` in here, we have no idea what it contains.
22112210
clusterPref.region = pref.region;
2212-
clusterPref.lrm = new Date().toISOString();
22132211

22142212
data.clusterPreferences = clusterPref;
22152213
user.additionalData = data;

0 commit comments

Comments
 (0)