fix: network reset navigation drift
This commit is contained in:
parent
47928ce9f2
commit
8a25474770
5 changed files with 73 additions and 16 deletions
|
|
@ -128,7 +128,7 @@ export default function useLatestWithSubscription(
|
||||||
|
|
||||||
// Some devices keep the WS transport "connected" after a lock/unlock, but the
|
// Some devices keep the WS transport "connected" after a lock/unlock, but the
|
||||||
// per-operation subscription stops delivering. Trigger a controlled resubscribe.
|
// per-operation subscription stops delivering. Trigger a controlled resubscribe.
|
||||||
const FOREGROUND_KICK_MIN_INACTIVE_MS = 3_000;
|
const FOREGROUND_KICK_MIN_INACTIVE_MS = 30_000;
|
||||||
const FOREGROUND_KICK_MIN_INTERVAL_MS = 15_000;
|
const FOREGROUND_KICK_MIN_INTERVAL_MS = 15_000;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|
@ -239,6 +239,15 @@ export default function useLatestWithSubscription(
|
||||||
if (age < livenessStaleMs) return;
|
if (age < livenessStaleMs) return;
|
||||||
|
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
const becameInactiveAt = lastBecameInactiveAtRef.current;
|
||||||
|
const inactiveWindowMs = becameInactiveAt ? now - becameInactiveAt : null;
|
||||||
|
if (
|
||||||
|
typeof inactiveWindowMs === "number" &&
|
||||||
|
inactiveWindowMs < livenessStaleMs + 15_000
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (now - lastLivenessKickAtRef.current < livenessStaleMs) return;
|
if (now - lastLivenessKickAtRef.current < livenessStaleMs) return;
|
||||||
lastLivenessKickAtRef.current = now;
|
lastLivenessKickAtRef.current = now;
|
||||||
|
|
||||||
|
|
@ -276,7 +285,7 @@ export default function useLatestWithSubscription(
|
||||||
|
|
||||||
// Escalation policy for repeated consecutive stale kicks.
|
// Escalation policy for repeated consecutive stale kicks.
|
||||||
if (
|
if (
|
||||||
consecutiveStaleKicksRef.current >= STALE_KICKS_BEFORE_RELOAD &&
|
consecutiveStaleKicksRef.current >= STALE_KICKS_BEFORE_RELOAD + 2 &&
|
||||||
now - lastReloadAtRef.current >= MIN_ESCALATION_INTERVAL_MS
|
now - lastReloadAtRef.current >= MIN_ESCALATION_INTERVAL_MS
|
||||||
) {
|
) {
|
||||||
const lastRecovery = wsLastRecoveryDateRef.current
|
const lastRecovery = wsLastRecoveryDateRef.current
|
||||||
|
|
@ -310,7 +319,7 @@ export default function useLatestWithSubscription(
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
networkActions.triggerReload();
|
networkActions.triggerReload("transport");
|
||||||
} else if (
|
} else if (
|
||||||
consecutiveStaleKicksRef.current >= STALE_KICKS_BEFORE_WS_RESTART &&
|
consecutiveStaleKicksRef.current >= STALE_KICKS_BEFORE_WS_RESTART &&
|
||||||
now - lastWsRestartAtRef.current >= MIN_ESCALATION_INTERVAL_MS
|
now - lastWsRestartAtRef.current >= MIN_ESCALATION_INTERVAL_MS
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ export default function useStreamQueryWithSubscription(
|
||||||
|
|
||||||
// Some devices keep the WS transport "connected" after a lock/unlock, but the
|
// Some devices keep the WS transport "connected" after a lock/unlock, but the
|
||||||
// per-operation subscription stops delivering. Trigger a controlled resubscribe.
|
// per-operation subscription stops delivering. Trigger a controlled resubscribe.
|
||||||
const FOREGROUND_KICK_MIN_INACTIVE_MS = 3_000;
|
const FOREGROUND_KICK_MIN_INACTIVE_MS = 30_000;
|
||||||
const FOREGROUND_KICK_MIN_INTERVAL_MS = 15_000;
|
const FOREGROUND_KICK_MIN_INTERVAL_MS = 15_000;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|
@ -281,6 +281,15 @@ export default function useStreamQueryWithSubscription(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Avoid spamming resubscribe triggers.
|
// Avoid spamming resubscribe triggers.
|
||||||
|
const becameInactiveAt = lastBecameInactiveAtRef.current;
|
||||||
|
const inactiveWindowMs = becameInactiveAt ? now - becameInactiveAt : null;
|
||||||
|
if (
|
||||||
|
typeof inactiveWindowMs === "number" &&
|
||||||
|
inactiveWindowMs < livenessStaleMs + 15_000
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (now - lastLivenessKickAtRef.current < livenessStaleMs) return;
|
if (now - lastLivenessKickAtRef.current < livenessStaleMs) return;
|
||||||
lastLivenessKickAtRef.current = now;
|
lastLivenessKickAtRef.current = now;
|
||||||
|
|
||||||
|
|
@ -318,7 +327,7 @@ export default function useStreamQueryWithSubscription(
|
||||||
|
|
||||||
// Escalation policy for repeated consecutive stale kicks.
|
// Escalation policy for repeated consecutive stale kicks.
|
||||||
if (
|
if (
|
||||||
consecutiveStaleKicksRef.current >= STALE_KICKS_BEFORE_RELOAD &&
|
consecutiveStaleKicksRef.current >= STALE_KICKS_BEFORE_RELOAD + 2 &&
|
||||||
now - lastReloadAtRef.current >= MIN_ESCALATION_INTERVAL_MS
|
now - lastReloadAtRef.current >= MIN_ESCALATION_INTERVAL_MS
|
||||||
) {
|
) {
|
||||||
const lastRecovery = wsLastRecoveryDateRef.current
|
const lastRecovery = wsLastRecoveryDateRef.current
|
||||||
|
|
@ -352,7 +361,7 @@ export default function useStreamQueryWithSubscription(
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
networkActions.triggerReload();
|
networkActions.triggerReload("transport");
|
||||||
} else if (
|
} else if (
|
||||||
consecutiveStaleKicksRef.current >= STALE_KICKS_BEFORE_WS_RESTART &&
|
consecutiveStaleKicksRef.current >= STALE_KICKS_BEFORE_WS_RESTART &&
|
||||||
now - lastWsRestartAtRef.current >= MIN_ESCALATION_INTERVAL_MS
|
now - lastWsRestartAtRef.current >= MIN_ESCALATION_INTERVAL_MS
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import getRetryMaxAttempts from "./getRetryMaxAttemps";
|
||||||
|
|
||||||
import { createLogger } from "~/lib/logger";
|
import { createLogger } from "~/lib/logger";
|
||||||
import { NETWORK_SCOPES } from "~/lib/logger/scopes";
|
import { NETWORK_SCOPES } from "~/lib/logger/scopes";
|
||||||
|
import createCache from "./cache";
|
||||||
|
|
||||||
const { useNetworkState, networkActions } = store;
|
const { useNetworkState, networkActions } = store;
|
||||||
|
|
||||||
|
|
@ -28,18 +29,23 @@ const networkProvidersLogger = createLogger({
|
||||||
feature: "NetworkProviders",
|
feature: "NetworkProviders",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const sharedApolloCache = createCache();
|
||||||
|
|
||||||
const initializeNewApolloClient = (reload) => {
|
const initializeNewApolloClient = (reload) => {
|
||||||
if (reload) {
|
if (reload) {
|
||||||
const { apolloClient } = network;
|
const { apolloClient } = network;
|
||||||
apolloClient.stop();
|
apolloClient.stop();
|
||||||
|
if (apolloClient.cache !== sharedApolloCache) {
|
||||||
apolloClient.clearStore();
|
apolloClient.clearStore();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
network.apolloClient = createApolloClient({
|
network.apolloClient = createApolloClient({
|
||||||
store,
|
store,
|
||||||
GRAPHQL_URL: env.GRAPHQL_URL,
|
GRAPHQL_URL: env.GRAPHQL_URL,
|
||||||
GRAPHQL_WS_URL: env.GRAPHQL_WS_URL,
|
GRAPHQL_WS_URL: env.GRAPHQL_WS_URL,
|
||||||
getRetryMaxAttempts,
|
getRetryMaxAttempts,
|
||||||
|
cache: sharedApolloCache,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
initializeNewApolloClient();
|
initializeNewApolloClient();
|
||||||
|
|
@ -51,34 +57,60 @@ network.oaFilesKy = oaFilesKy;
|
||||||
|
|
||||||
export default function NetworkProviders({ children }) {
|
export default function NetworkProviders({ children }) {
|
||||||
const [key, setKey] = useState(0);
|
const [key, setKey] = useState(0);
|
||||||
|
const [transportClient, setTransportClient] = useState(() => network.apolloClient);
|
||||||
|
|
||||||
const networkState = useNetworkState(["initialized", "triggerReload"]);
|
const networkState = useNetworkState([
|
||||||
|
"initialized",
|
||||||
|
"triggerReload",
|
||||||
|
"reloadKind",
|
||||||
|
"transportGeneration",
|
||||||
|
]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (networkState.triggerReload) {
|
if (networkState.triggerReload) {
|
||||||
networkProvidersLogger.debug("Network triggerReload received", {
|
networkProvidersLogger.debug("Network triggerReload received", {
|
||||||
|
reloadKind: networkState.reloadKind,
|
||||||
reloadId: store.getAuthState()?.reloadId,
|
reloadId: store.getAuthState()?.reloadId,
|
||||||
hasUserToken: !!store.getAuthState()?.userToken,
|
hasUserToken: !!store.getAuthState()?.userToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isFullReload = networkState.reloadKind !== "transport";
|
||||||
initializeNewApolloClient(true);
|
initializeNewApolloClient(true);
|
||||||
|
|
||||||
|
if (isFullReload) {
|
||||||
|
setTransportClient(network.apolloClient);
|
||||||
setKey((prevKey) => prevKey + 1);
|
setKey((prevKey) => prevKey + 1);
|
||||||
|
} else {
|
||||||
|
setTransportClient(network.apolloClient);
|
||||||
|
networkProvidersLogger.debug("Network transport recovered in place", {
|
||||||
|
reloadId: store.getAuthState()?.reloadId,
|
||||||
|
hasUserToken: !!store.getAuthState()?.userToken,
|
||||||
|
transportGeneration: networkState.transportGeneration,
|
||||||
|
});
|
||||||
|
networkActions.onReload();
|
||||||
}
|
}
|
||||||
}, [networkState.triggerReload]);
|
}
|
||||||
|
}, [
|
||||||
|
networkState.triggerReload,
|
||||||
|
networkState.reloadKind,
|
||||||
|
networkState.transportGeneration,
|
||||||
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (key > 0) {
|
if (key > 0) {
|
||||||
networkProvidersLogger.debug("Network reloaded", {
|
networkProvidersLogger.debug("Network reloaded", {
|
||||||
|
reloadKind: networkState.reloadKind,
|
||||||
reloadId: store.getAuthState()?.reloadId,
|
reloadId: store.getAuthState()?.reloadId,
|
||||||
hasUserToken: !!store.getAuthState()?.userToken,
|
hasUserToken: !!store.getAuthState()?.userToken,
|
||||||
});
|
});
|
||||||
networkActions.onReload();
|
networkActions.onReload();
|
||||||
}
|
}
|
||||||
}, [key]);
|
}, [key, networkState.reloadKind]);
|
||||||
|
|
||||||
if (!networkState.initialized) {
|
if (!networkState.initialized) {
|
||||||
return <Loader />;
|
return <Loader />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const providers = [[ApolloProvider, { client: network.apolloClient }]];
|
const providers = [[ApolloProvider, { client: transportClient }]];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ComposeComponents key={key} components={providers}>
|
<ComposeComponents key={key} components={providers}>
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ if (__DEV__ || process.env.NODE_ENV !== "production") {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function createApolloClient(options) {
|
export default function createApolloClient(options) {
|
||||||
|
const cache = options.cache || createCache();
|
||||||
const errorLink = createErrorLink(options);
|
const errorLink = createErrorLink(options);
|
||||||
const authLink = createAuthLink(options);
|
const authLink = createAuthLink(options);
|
||||||
const cancelLink = createCancelLink();
|
const cancelLink = createCancelLink();
|
||||||
|
|
@ -50,8 +51,6 @@ export default function createApolloClient(options) {
|
||||||
httpLink: httpChain,
|
httpLink: httpChain,
|
||||||
});
|
});
|
||||||
|
|
||||||
const cache = createCache();
|
|
||||||
|
|
||||||
const apolloClient = new ApolloClient({
|
const apolloClient = new ApolloClient({
|
||||||
cache,
|
cache,
|
||||||
// connectToDevTools: true, // Enable dev tools for better debugging
|
// connectToDevTools: true, // Enable dev tools for better debugging
|
||||||
|
|
|
||||||
|
|
@ -9,20 +9,28 @@ export default createAtom(({ merge, get }) => {
|
||||||
wsLastHeartbeatDate: null,
|
wsLastHeartbeatDate: null,
|
||||||
wsLastRecoveryDate: null,
|
wsLastRecoveryDate: null,
|
||||||
triggerReload: false,
|
triggerReload: false,
|
||||||
|
reloadKind: null,
|
||||||
initialized: true,
|
initialized: true,
|
||||||
hasInternetConnection: true,
|
hasInternetConnection: true,
|
||||||
|
transportGeneration: 0,
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
triggerReload: () => {
|
triggerReload: (reloadKind = "full") => {
|
||||||
merge({
|
merge({
|
||||||
initialized: false,
|
|
||||||
triggerReload: true,
|
triggerReload: true,
|
||||||
|
reloadKind,
|
||||||
|
initialized: reloadKind === "transport" ? true : false,
|
||||||
|
transportGeneration:
|
||||||
|
reloadKind === "transport"
|
||||||
|
? get("transportGeneration") + 1
|
||||||
|
: get("transportGeneration"),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onReload: () => {
|
onReload: () => {
|
||||||
merge({
|
merge({
|
||||||
initialized: true,
|
initialized: true,
|
||||||
triggerReload: false,
|
triggerReload: false,
|
||||||
|
reloadKind: null,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
WSConnected: () => {
|
WSConnected: () => {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue