-
Notifications
You must be signed in to change notification settings - Fork 894
feat: Auto select workspace proxy based on lowest latency #7515
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
159538c
1305931
d1c9f3f
c739f24
ae26777
523e492
877c0c9
8d26643
5c534fa
a3703e1
c210e8d
a358258
ec860c3
3197eb1
ebb6a76
549093e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,9 +32,6 @@ export interface ProxyContextValue { | |
// The value `proxy` should always be used instead of `userProxy`. `userProxy` is only exposed | ||
// so the caller can determine if the proxy being used is the user's selected proxy, or if it | ||
// was auto selected based on some other criteria. | ||
// | ||
// if(proxy.selectedProxy.id === userProxy.id) { /* user selected proxy */ } | ||
// else { /* proxy was auto selected */ } | ||
userProxy?: Region | ||
|
||
// proxies is the list of proxies returned by coderd. This is fetched async. | ||
|
@@ -79,27 +76,18 @@ export const ProxyContext = createContext<ProxyContextValue | undefined>( | |
* ProxyProvider interacts with local storage to indicate the preferred workspace proxy. | ||
*/ | ||
export const ProxyProvider: FC<PropsWithChildren> = ({ children }) => { | ||
// Try to load the preferred proxy from local storage. | ||
const [savedProxy, setUserProxy] = useState<Region | undefined>( | ||
loadUserSelectedProxy(), | ||
) | ||
|
||
// As the proxies are being loaded, default to using the saved proxy. | ||
// If the saved proxy is not valid when the async fetch happens, the | ||
// proxy will be updated accordingly. | ||
let defaultPreferredProxy = computeUsableURLS(savedProxy) | ||
const dashboard = useDashboard() | ||
const experimentEnabled = dashboard?.experiments.includes("moons") | ||
|
||
if (!savedProxy) { | ||
// If no preferred proxy is saved, then default to using relative paths | ||
// and no subdomain support until the proxies are properly loaded. | ||
// This is the same as a user not selecting any proxy. | ||
defaultPreferredProxy = getPreferredProxy([]) | ||
} | ||
// Using a useState so the caller always has the latest user saved | ||
// proxy. | ||
const [userSavedProxy, setUserSavedProxy] = useState(loadUserSelectedProxy()) | ||
|
||
const [proxy, setProxy] = useState<PreferredProxy>(defaultPreferredProxy) | ||
// Load the initial state from local storage. | ||
const [proxy, setProxy] = useState<PreferredProxy>( | ||
computeUsableURLS(userSavedProxy), | ||
) | ||
|
||
const dashboard = useDashboard() | ||
const experimentEnabled = dashboard?.experiments.includes("moons") | ||
const queryKey = ["get-proxies"] | ||
const { | ||
data: proxiesResp, | ||
|
@@ -109,57 +97,39 @@ export const ProxyProvider: FC<PropsWithChildren> = ({ children }) => { | |
} = useQuery({ | ||
queryKey, | ||
queryFn: getWorkspaceProxies, | ||
// This onSuccess ensures the local storage is synchronized with the | ||
// proxies returned by coderd. If the selected proxy is not in the list, | ||
// then the user selection is ignored. | ||
onSuccess: (resp) => { | ||
// Always pass in the user's choice. | ||
setAndSaveProxy(savedProxy, resp.regions) | ||
}, | ||
}) | ||
|
||
// Every time we get a new proxiesResponse, update the latency check | ||
// to each workspace proxy. | ||
const proxyLatencies = useProxyLatency(proxiesResp) | ||
|
||
// If the proxies change or latencies change, we need to update the | ||
// callback function. | ||
const setAndSaveProxy = useCallback( | ||
( | ||
selectedProxy?: Region, | ||
// By default the proxies come from the api call above. | ||
// Allow the caller to override this if they have a more up | ||
// to date list of proxies. | ||
proxies: Region[] = proxiesResp?.regions || [], | ||
) => { | ||
if (!proxies) { | ||
throw new Error( | ||
"proxies are not yet loaded, so selecting a proxy makes no sense. How did you get here?", | ||
) | ||
} | ||
|
||
// The preferred proxy attempts to use the user's selection if it is valid. | ||
const preferred = getPreferredProxy( | ||
proxies, | ||
selectedProxy, | ||
// updateProxy is a helper function that when called will | ||
// update the proxy being used. | ||
const updateProxy = useCallback(() => { | ||
// Update the saved user proxy for the caller. | ||
setUserSavedProxy(loadUserSelectedProxy()) | ||
Kira-Pilot marked this conversation as resolved.
Show resolved
Hide resolved
|
||
setProxy( | ||
getPreferredProxy( | ||
proxiesResp?.regions ?? [], | ||
// For some reason if I use 'userSavedProxy' here, | ||
// the tests fail. It does not load "undefined" after a | ||
// clear, but takes the previous value. | ||
loadUserSelectedProxy(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried adding an Ln 110 only loads from local storage, it does not do any writes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My bad about ln 110 - was confusing Regarding your |
||
proxyLatencies, | ||
) | ||
// Set the state for the current context. | ||
setProxy(preferred) | ||
}, | ||
[proxiesResp, proxyLatencies], | ||
) | ||
), | ||
) | ||
}, [userSavedProxy, proxiesResp, proxyLatencies]) | ||
Emyrk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// This useEffect ensures the proxy to be used is updated whenever the state changes. | ||
// This includes proxies being loaded, latencies being calculated, and the user selecting a proxy. | ||
useEffect(() => { | ||
setAndSaveProxy(savedProxy) | ||
}, [savedProxy, proxiesResp, proxyLatencies, setAndSaveProxy]) | ||
updateProxy() | ||
}, [proxiesResp, proxyLatencies]) | ||
|
||
return ( | ||
<ProxyContext.Provider | ||
value={{ | ||
userProxy: savedProxy, | ||
userProxy: userSavedProxy, | ||
proxyLatencies: proxyLatencies, | ||
proxy: experimentEnabled | ||
? proxy | ||
|
@@ -177,13 +147,13 @@ export const ProxyProvider: FC<PropsWithChildren> = ({ children }) => { | |
setProxy: (proxy: Region) => { | ||
// Save to local storage to persist the user's preference across reloads | ||
saveUserSelectedProxy(proxy) | ||
// Set the state for the current context. | ||
setUserProxy(proxy) | ||
// Update the selected proxy | ||
updateProxy() | ||
}, | ||
clearProxy: () => { | ||
// Clear the user's selection from local storage. | ||
clearUserSelectedProxy() | ||
setUserProxy(undefined) | ||
updateProxy() | ||
}, | ||
}} | ||
> | ||
|
Uh oh!
There was an error while loading. Please reload this page.