@@ -4,6 +4,8 @@ import PerformanceObserver from "@fastly/performance-observer-polyfill"
4
4
import axios from "axios"
5
5
import { generateRandomString } from "utils/random"
6
6
7
+ const proxyIntervalSeconds = 30 // seconds
8
+
7
9
export interface ProxyLatencyReport {
8
10
// accurate identifies if the latency was calculated using the
9
11
// PerformanceResourceTiming API. If this is false, then the
@@ -17,6 +19,8 @@ export interface ProxyLatencyReport {
17
19
18
20
interface ProxyLatencyAction {
19
21
proxyID : string
22
+ // cached indicates if the latency was loaded from a cache (local storage)
23
+ cached : boolean
20
24
report : ProxyLatencyReport
21
25
}
22
26
@@ -59,8 +63,13 @@ export const useProxyLatency = (
59
63
60
64
// This latestFetchRequest is used to trigger a refetch of the proxy latencies.
61
65
const [ latestFetchRequest , setLatestFetchRequest ] = useState (
62
- new Date ( ) . toISOString ( ) ,
66
+ // The initial state is the current time minus the interval. Any proxies that have a latency after this
67
+ // in the cache are still valid.
68
+ new Date ( new Date ( ) . getTime ( ) - proxyIntervalSeconds * 1000 ) . toISOString ( ) ,
63
69
)
70
+
71
+ // Refetch will always set the latestFetchRequest to the current time, making all the cached latencies
72
+ // stale and triggering a refetch of all proxies in the list.
64
73
const refetch = ( ) => {
65
74
const d = new Date ( )
66
75
setLatestFetchRequest ( d . toISOString ( ) )
@@ -73,6 +82,8 @@ export const useProxyLatency = (
73
82
return
74
83
}
75
84
85
+ const storedLatencies = loadStoredLatencies ( )
86
+
76
87
// proxyMap is a map of the proxy path_app_url to the proxy object.
77
88
// This is for the observer to know which requests are important to
78
89
// record.
@@ -82,6 +93,28 @@ export const useProxyLatency = (
82
93
return acc
83
94
}
84
95
96
+ // Do not run latency checks if a cached check exists below the latestFetchRequest Date.
97
+ // This prevents fetching latencies too often.
98
+ // 1. Fetch the latest stored latency for the given proxy.
99
+ // 2. If the latest latency is after the latestFetchRequest, then skip the latency check.
100
+ if ( storedLatencies && storedLatencies [ proxy . id ] ) {
101
+ const fetchRequestDate = new Date ( latestFetchRequest )
102
+ const latest = storedLatencies [ proxy . id ] . reduce ( ( prev , next ) =>
103
+ prev . at > next . at ? prev : next ,
104
+ )
105
+
106
+ if ( latest && latest . at > fetchRequestDate ) {
107
+ // dispatch the cached latency. This latency already went through the
108
+ // guard logic below, so we can just dispatch it again directly.
109
+ dispatchProxyLatencies ( {
110
+ proxyID : proxy . id ,
111
+ cached : true ,
112
+ report : latest ,
113
+ } )
114
+ return acc
115
+ }
116
+ }
117
+
85
118
// Add a random query param to the url to make sure we don't get a cached response.
86
119
// This is important in case there is some caching layer between us and the proxy.
87
120
const url = new URL (
@@ -131,6 +164,7 @@ export const useProxyLatency = (
131
164
}
132
165
const update = {
133
166
proxyID : check . id ,
167
+ cached : false ,
134
168
report : {
135
169
latencyMS,
136
170
accurate,
@@ -203,7 +237,13 @@ const loadStoredLatencies = (): Record<string, ProxyLatencyReport[]> => {
203
237
return { }
204
238
}
205
239
206
- return JSON . parse ( str )
240
+ return JSON . parse ( str , ( key , value ) => {
241
+ // By default json loads dates as strings. We want to convert them back to 'Date's.
242
+ if ( key === "at" ) {
243
+ return new Date ( value )
244
+ }
245
+ return value
246
+ } )
207
247
}
208
248
209
249
const updateStoredLatencies = ( action : ProxyLatencyAction ) : void => {
0 commit comments