Skip to content

Commit 66399ae

Browse files
committed
Switch to proxy latency report
1 parent c2638ab commit 66399ae

File tree

5 files changed

+53
-29
lines changed

5 files changed

+53
-29
lines changed

site/src/contexts/ProxyContext.tsx

+4-5
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,12 @@ import {
99
useContext,
1010
useState,
1111
} from "react"
12-
import { useProxyLatency } from "./useProxyLatency"
12+
import { ProxyLatencyReport, useProxyLatency } from "./useProxyLatency"
1313

1414
interface ProxyContextValue {
1515
proxy: PreferredProxy
1616
proxies?: Region[]
17-
// proxyLatenciesMS are recorded in milliseconds.
18-
proxyLatenciesMS?: Record<string, number>
17+
proxyLatencies?: Record<string, ProxyLatencyReport>
1918
// isfetched is true when the proxy api call is complete.
2019
isFetched: boolean
2120
// isLoading is true if the proxy is in the process of being fetched.
@@ -77,7 +76,7 @@ export const ProxyProvider: FC<PropsWithChildren> = ({ children }) => {
7776

7877
// Everytime we get a new proxiesResponse, update the latency check
7978
// to each workspace proxy.
80-
const proxyLatenciesMS = useProxyLatency(proxiesResp)
79+
const proxyLatencies = useProxyLatency(proxiesResp)
8180

8281
const setAndSaveProxy = (
8382
selectedProxy?: Region,
@@ -102,7 +101,7 @@ export const ProxyProvider: FC<PropsWithChildren> = ({ children }) => {
102101
return (
103102
<ProxyContext.Provider
104103
value={{
105-
proxyLatenciesMS: proxyLatenciesMS,
104+
proxyLatencies: proxyLatencies,
106105
proxy: experimentEnabled
107106
? proxy
108107
: {

site/src/contexts/useProxyLatency.ts

+37-16
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,36 @@ import { Region, RegionsResponse } from "api/typesGenerated";
22
import { useEffect, useReducer } from "react";
33
import PerformanceObserver from "@fastly/performance-observer-polyfill"
44
import axios from "axios";
5+
import { generateRandomString } from "utils/random";
56

67

8+
export interface ProxyLatencyReport {
9+
// accurate identifies if the latency was calculated using the
10+
// PerformanceResourceTiming API. If this is false, then the
11+
// latency is calculated using the total duration of the request
12+
// and will be off by a good margin.
13+
accurate: boolean
14+
latencyMS: number
15+
// at is when the latency was recorded.
16+
at: Date
17+
}
18+
719
interface ProxyLatencyAction {
820
proxyID: string
9-
latencyMS: number
21+
report: ProxyLatencyReport
1022
}
1123

1224
const proxyLatenciesReducer = (
13-
state: Record<string, number>,
25+
state: Record<string, ProxyLatencyReport>,
1426
action: ProxyLatencyAction,
15-
): Record<string, number> => {
27+
): Record<string, ProxyLatencyReport> => {
1628
// Just overwrite any existing latency.
17-
state[action.proxyID] = action.latencyMS
29+
state[action.proxyID] = action.report
1830
return state
1931
}
2032

21-
export const useProxyLatency = (proxies?: RegionsResponse): Record<string, number> => {
22-
const [proxyLatenciesMS, dispatchProxyLatenciesMS] = useReducer(
33+
export const useProxyLatency = (proxies?: RegionsResponse): Record<string, ProxyLatencyReport> => {
34+
const [proxyLatencies, dispatchProxyLatencies] = useReducer(
2335
proxyLatenciesReducer,
2436
{},
2537
);
@@ -34,20 +46,23 @@ export const useProxyLatency = (proxies?: RegionsResponse): Record<string, numbe
3446
// This is for the observer to know which requests are important to
3547
// record.
3648
const proxyChecks = proxies.regions.reduce((acc, proxy) => {
49+
// Only run the latency check on healthy proxies.
3750
if (!proxy.healthy) {
3851
return acc
3952
}
4053

41-
const url = new URL("/latency-check", proxy.path_app_url)
54+
// Add a random query param to the url to make sure we don't get a cached response.
55+
// This is important in case there is some caching layer between us and the proxy.
56+
const url = new URL(`/latency-check?cache_bust=${generateRandomString(6)}`, proxy.path_app_url)
4257
acc[url.toString()] = proxy
4358
return acc
4459
}, {} as Record<string, Region>)
4560

4661

47-
// dispatchProxyLatenciesMSGuarded will assign the latency to the proxy
62+
// dispatchProxyLatenciesGuarded will assign the latency to the proxy
4863
// via the reducer. But it will only do so if the performance entry is
4964
// a resource entry that we care about.
50-
const dispatchProxyLatenciesMSGuarded = (entry:PerformanceEntry):void => {
65+
const dispatchProxyLatenciesGuarded = (entry:PerformanceEntry):void => {
5166
if (entry.entryType !== "resource") {
5267
// We should never get these, but just in case.
5368
return
@@ -56,26 +71,32 @@ export const useProxyLatency = (proxies?: RegionsResponse): Record<string, numbe
5671
// The entry.name is the url of the request.
5772
const check = proxyChecks[entry.name]
5873
if (!check) {
59-
// This is not a proxy request.
74+
// This is not a proxy request, so ignore it.
6075
return
6176
}
6277

6378
// These docs are super useful.
6479
// https://developer.mozilla.org/en-US/docs/Web/API/Performance_API/Resource_timing
6580
let latencyMS = 0
81+
let accurate = false
6682
if("requestStart" in entry && (entry as PerformanceResourceTiming).requestStart !== 0) {
6783
// This is the preferred logic to get the latency.
6884
const timingEntry = entry as PerformanceResourceTiming
69-
latencyMS = timingEntry.responseEnd - timingEntry.requestStart
85+
latencyMS = timingEntry.responseStart - timingEntry.requestStart
86+
accurate = true
7087
} else {
7188
// This is the total duration of the request and will be off by a good margin.
7289
// This is a fallback if the better timing is not available.
7390
console.log(`Using fallback latency calculation for "${entry.name}". Latency will be incorrect and larger then actual.`)
7491
latencyMS = entry.duration
7592
}
76-
dispatchProxyLatenciesMS({
93+
dispatchProxyLatencies({
7794
proxyID: check.id,
78-
latencyMS: latencyMS,
95+
report: {
96+
latencyMS,
97+
accurate,
98+
at: new Date(),
99+
},
79100
})
80101

81102
return
@@ -86,7 +107,7 @@ export const useProxyLatency = (proxies?: RegionsResponse): Record<string, numbe
86107
const observer = new PerformanceObserver((list) => {
87108
// If we get entries via this callback, then dispatch the events to the latency reducer.
88109
list.getEntries().forEach((entry) => {
89-
dispatchProxyLatenciesMSGuarded(entry)
110+
dispatchProxyLatenciesGuarded(entry)
90111
})
91112
})
92113

@@ -112,13 +133,13 @@ export const useProxyLatency = (proxies?: RegionsResponse): Record<string, numbe
112133
// We want to call this before we disconnect the observer to make sure we get all the
113134
// proxy requests recorded.
114135
observer.takeRecords().forEach((entry) => {
115-
dispatchProxyLatenciesMSGuarded(entry)
136+
dispatchProxyLatenciesGuarded(entry)
116137
})
117138
// At this point, we can be confident that all the proxy requests have been recorded
118139
// via the performance observer. So we can disconnect the observer.
119140
observer.disconnect()
120141
})
121142
}, [proxies])
122143

123-
return proxyLatenciesMS
144+
return proxyLatencies
124145
}

site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyPage.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export const WorkspaceProxyPage: FC<PropsWithChildren<unknown>> = () => {
1414
"This selection only affects browser connections to your workspace."
1515

1616
const {
17-
proxyLatenciesMS,
17+
proxyLatencies,
1818
proxies,
1919
error: proxiesError,
2020
isFetched: proxiesFetched,
@@ -31,7 +31,7 @@ export const WorkspaceProxyPage: FC<PropsWithChildren<unknown>> = () => {
3131
layout="fluid"
3232
>
3333
<WorkspaceProxyView
34-
proxyLatenciesMS={proxyLatenciesMS}
34+
proxyLatencies={proxyLatencies}
3535
proxies={proxies}
3636
isLoading={proxiesLoading}
3737
hasLoaded={proxiesFetched}

site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyRow.tsx

+6-3
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ import {
1111
} from "components/DeploySettingsLayout/Badges"
1212
import { makeStyles } from "@material-ui/core/styles"
1313
import { combineClasses } from "utils/combineClasses"
14+
import { ProxyLatencyReport } from "contexts/useProxyLatency"
1415

1516
export const ProxyRow: FC<{
16-
latencyMS?: number
17+
latency?: ProxyLatencyReport
1718
proxy: Region
1819
onSelectRegion: (proxy: Region) => void
1920
preferred: boolean
20-
}> = ({ proxy, onSelectRegion, preferred, latencyMS }) => {
21+
}> = ({ proxy, onSelectRegion, preferred, latency }) => {
2122
const styles = useStyles()
2223

2324
const clickable = useClickableTableRow(() => {
@@ -54,7 +55,9 @@ export const ProxyRow: FC<{
5455
<TableCell>
5556
<ProxyStatus proxy={proxy} />
5657
</TableCell>
57-
<TableCell>{latencyMS ? `${latencyMS.toFixed(1)} ms` : "?"}</TableCell>
58+
<TableCell>
59+
{latency ? `${latency.latencyMS.toFixed(1)} ms` : "?"}
60+
</TableCell>
5861
</TableRow>
5962
)
6063
}

site/src/pages/UserSettingsPage/WorkspaceProxyPage/WorkspaceProxyView.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ import { FC } from "react"
1212
import { AlertBanner } from "components/AlertBanner/AlertBanner"
1313
import { Region } from "api/typesGenerated"
1414
import { ProxyRow } from "./WorkspaceProxyRow"
15+
import { ProxyLatencyReport } from "contexts/useProxyLatency"
1516

1617
export interface WorkspaceProxyViewProps {
1718
proxies?: Region[]
18-
proxyLatenciesMS?: Record<string, number>
19+
proxyLatencies?: Record<string, ProxyLatencyReport>
1920
getWorkspaceProxiesError?: Error | unknown
2021
isLoading: boolean
2122
hasLoaded: boolean
@@ -28,7 +29,7 @@ export const WorkspaceProxyView: FC<
2829
React.PropsWithChildren<WorkspaceProxyViewProps>
2930
> = ({
3031
proxies,
31-
proxyLatenciesMS,
32+
proxyLatencies,
3233
getWorkspaceProxiesError,
3334
isLoading,
3435
hasLoaded,
@@ -64,7 +65,7 @@ export const WorkspaceProxyView: FC<
6465
<Cond>
6566
{proxies?.map((proxy) => (
6667
<ProxyRow
67-
latencyMS={proxyLatenciesMS?.[proxy.id]}
68+
latency={proxyLatencies?.[proxy.id]}
6869
key={proxy.id}
6970
proxy={proxy}
7071
onSelectRegion={onSelect}

0 commit comments

Comments
 (0)