fix(ws): stabilization try 6 + typo
This commit is contained in:
parent
ef643f77cb
commit
5dfb064c2c
5 changed files with 59 additions and 4 deletions
|
|
@ -3,6 +3,7 @@ import useStreamQueryWithSubscription from "~/hooks/useStreamQueryWithSubscripti
|
||||||
import { aggregatedMessagesActions } from "~/stores";
|
import { aggregatedMessagesActions } from "~/stores";
|
||||||
import { createLogger } from "~/lib/logger";
|
import { createLogger } from "~/lib/logger";
|
||||||
import { FEATURE_SCOPES, NETWORK_SCOPES } from "~/lib/logger/scopes";
|
import { FEATURE_SCOPES, NETWORK_SCOPES } from "~/lib/logger/scopes";
|
||||||
|
import { AppState } from "react-native";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AGGREGATED_MESSAGES_QUERY,
|
AGGREGATED_MESSAGES_QUERY,
|
||||||
|
|
@ -17,12 +18,14 @@ const messagesLogger = createLogger({
|
||||||
const AggregatedMessagesSubscription = () => {
|
const AggregatedMessagesSubscription = () => {
|
||||||
// Ref to track if we've already run the cleanup
|
// Ref to track if we've already run the cleanup
|
||||||
const initRunRef = useRef(false);
|
const initRunRef = useRef(false);
|
||||||
|
const lastForegroundCatchupAtRef = useRef(0);
|
||||||
|
|
||||||
// Aggregated messages subscription
|
// Aggregated messages subscription
|
||||||
const {
|
const {
|
||||||
data: messagesData,
|
data: messagesData,
|
||||||
error: messagesError,
|
error: messagesError,
|
||||||
loading,
|
loading,
|
||||||
|
refetch,
|
||||||
} = useStreamQueryWithSubscription(
|
} = useStreamQueryWithSubscription(
|
||||||
AGGREGATED_MESSAGES_QUERY,
|
AGGREGATED_MESSAGES_QUERY,
|
||||||
AGGREGATED_MESSAGES_SUBSCRIPTION,
|
AGGREGATED_MESSAGES_SUBSCRIPTION,
|
||||||
|
|
@ -47,6 +50,37 @@ const AggregatedMessagesSubscription = () => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Foreground catch-up: on mobile, WS can take time to resume after background.
|
||||||
|
// Do a lightweight refresh shortly after the app becomes active.
|
||||||
|
useEffect(() => {
|
||||||
|
const sub = AppState.addEventListener("change", (next) => {
|
||||||
|
if (next !== "active") return;
|
||||||
|
|
||||||
|
const now = Date.now();
|
||||||
|
// Avoid spamming refetches if the app toggles state quickly.
|
||||||
|
if (now - lastForegroundCatchupAtRef.current < 15_000) return;
|
||||||
|
lastForegroundCatchupAtRef.current = now;
|
||||||
|
|
||||||
|
if (!refetch) return;
|
||||||
|
try {
|
||||||
|
messagesLogger.info(
|
||||||
|
"Foreground catch-up: refetching aggregated messages",
|
||||||
|
);
|
||||||
|
Promise.resolve()
|
||||||
|
.then(() => refetch())
|
||||||
|
.catch((e) => {
|
||||||
|
messagesLogger.warn("Foreground catch-up refetch failed", {
|
||||||
|
error: e?.message,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (_e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => sub.remove();
|
||||||
|
}, [refetch]);
|
||||||
|
|
||||||
// Update loading state
|
// Update loading state
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
aggregatedMessagesActions.setLoading(loading);
|
aggregatedMessagesActions.setLoading(loading);
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,12 @@ export default function useLatestWithSubscription(
|
||||||
`[${subscriptionKey}] WS reconnect detected, refetching base query to prevent gaps`,
|
`[${subscriptionKey}] WS reconnect detected, refetching base query to prevent gaps`,
|
||||||
{ wsClosedDate },
|
{ wsClosedDate },
|
||||||
);
|
);
|
||||||
await refetch();
|
// Don't block re-subscription forever if refetch is slow/stuck.
|
||||||
|
const maxWaitMs = 8000;
|
||||||
|
await Promise.race([
|
||||||
|
Promise.resolve().then(() => refetch()),
|
||||||
|
new Promise((resolve) => setTimeout(resolve, maxWaitMs)),
|
||||||
|
]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`[${subscriptionKey}] Refetch-on-reconnect failed (continuing with resubscribe)`,
|
`[${subscriptionKey}] Refetch-on-reconnect failed (continuing with resubscribe)`,
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,12 @@ export default function useStreamQueryWithSubscription(
|
||||||
`[${subscriptionKey}] WS reconnect detected, refetching base query to prevent gaps`,
|
`[${subscriptionKey}] WS reconnect detected, refetching base query to prevent gaps`,
|
||||||
{ wsClosedDate },
|
{ wsClosedDate },
|
||||||
);
|
);
|
||||||
await refetch();
|
// Don't block re-subscription forever if refetch is slow/stuck.
|
||||||
|
const maxWaitMs = 8000;
|
||||||
|
await Promise.race([
|
||||||
|
Promise.resolve().then(() => refetch()),
|
||||||
|
new Promise((resolve) => setTimeout(resolve, maxWaitMs)),
|
||||||
|
]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`[${subscriptionKey}] Refetch-on-reconnect failed (continuing with resubscribe)`,
|
`[${subscriptionKey}] Refetch-on-reconnect failed (continuing with resubscribe)`,
|
||||||
|
|
@ -800,5 +805,6 @@ export default function useStreamQueryWithSubscription(
|
||||||
data: queryData,
|
data: queryData,
|
||||||
loading,
|
loading,
|
||||||
error,
|
error,
|
||||||
|
refetch,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { print } from "graphql";
|
||||||
// import { createClient } from "graphql-ws";
|
// import { createClient } from "graphql-ws";
|
||||||
import { createRestartableClient } from "./graphqlWs";
|
import { createRestartableClient } from "./graphqlWs";
|
||||||
import network from "~/network";
|
import network from "~/network";
|
||||||
|
import { networkActions } from "~/stores";
|
||||||
|
|
||||||
export default class WebSocketLink extends ApolloLink {
|
export default class WebSocketLink extends ApolloLink {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
|
|
@ -18,7 +19,16 @@ export default class WebSocketLink extends ApolloLink {
|
||||||
return this.client.subscribe(
|
return this.client.subscribe(
|
||||||
{ ...operation, query: print(operation.query) },
|
{ ...operation, query: print(operation.query) },
|
||||||
{
|
{
|
||||||
next: sink.next.bind(sink),
|
next: (value) => {
|
||||||
|
// Any subscription payload means the transport is alive.
|
||||||
|
// Touch WS heartbeat so watchdog/liveness logic doesn't falsely conclude staleness.
|
||||||
|
try {
|
||||||
|
networkActions.WSTouch();
|
||||||
|
} catch (_e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
sink.next(value);
|
||||||
|
},
|
||||||
complete: sink.complete.bind(sink),
|
complete: sink.complete.bind(sink),
|
||||||
error: (err) => {
|
error: (err) => {
|
||||||
// Don't propagate client restart events as errors
|
// Don't propagate client restart events as errors
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ export default createAtom(({ merge, getActions }) => {
|
||||||
const networkActions = getActions("network");
|
const networkActions = getActions("network");
|
||||||
|
|
||||||
const alertActions = getActions("alert");
|
const alertActions = getActions("alert");
|
||||||
const navActions = getActions("alert");
|
const navActions = getActions("nav");
|
||||||
const fcmActions = getActions("fcm");
|
const fcmActions = getActions("fcm");
|
||||||
const paramsActions = getActions("params");
|
const paramsActions = getActions("params");
|
||||||
const notificationsActions = getActions("notifications");
|
const notificationsActions = getActions("notifications");
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue