























@@ -1,12 +1,87 @@
1+import type { ChannelAccountSnapshot } from "../../channels/plugins/types.public.js";
2+import type { ChannelHealthSummary, HealthSummary } from "../../commands/health.types.js";
13import { getStatusSummary } from "../../commands/status.js";
24import { ErrorCodes, errorShape } from "../protocol/index.js";
5+import type { ChannelRuntimeSnapshot } from "../server-channel-runtime.types.js";
36import { HEALTH_REFRESH_INTERVAL_MS } from "../server-constants.js";
47import { formatError } from "../server-utils.js";
58import { formatForLog } from "../ws-log.js";
69import type { GatewayRequestHandlers } from "./types.js";
710811const ADMIN_SCOPE = "operator.admin";
91213+function cachedAccountForRuntimeSnapshot(params: {
14+cachedChannel: ChannelHealthSummary | undefined;
15+accountId: string | undefined;
16+}): ChannelHealthSummary | undefined {
17+const accountId = params.accountId;
18+if (accountId && params.cachedChannel?.accounts?.[accountId]) {
19+return params.cachedChannel.accounts[accountId];
20+}
21+return undefined;
22+}
23+24+function cachedLifecycleDiffersFromRuntime(params: {
25+cachedAccount: ChannelHealthSummary | undefined;
26+runtimeSnapshot: ChannelAccountSnapshot;
27+}): boolean {
28+for (const key of ["running", "connected"] as const) {
29+const runtimeValue = params.runtimeSnapshot[key];
30+if (typeof runtimeValue !== "boolean") {
31+continue;
32+}
33+if (params.cachedAccount?.[key] !== runtimeValue) {
34+return true;
35+}
36+}
37+return false;
38+}
39+40+function cachedHealthDiffersFromRuntime(
41+cached: HealthSummary,
42+runtime: ChannelRuntimeSnapshot,
43+): boolean {
44+for (const [channelId, runtimeSnapshot] of Object.entries(runtime.channels)) {
45+if (!runtimeSnapshot) {
46+continue;
47+}
48+const cachedChannel = cached.channels[channelId];
49+if (
50+cachedLifecycleDiffersFromRuntime({
51+cachedAccount: cachedChannel,
52+ runtimeSnapshot,
53+})
54+) {
55+return true;
56+}
57+}
58+59+for (const [channelId, accounts] of Object.entries(runtime.channelAccounts)) {
60+if (!accounts) {
61+continue;
62+}
63+const cachedChannel = cached.channels[channelId];
64+for (const [accountId, runtimeSnapshot] of Object.entries(accounts)) {
65+if (!runtimeSnapshot) {
66+continue;
67+}
68+if (
69+cachedLifecycleDiffersFromRuntime({
70+cachedAccount: cachedAccountForRuntimeSnapshot({
71+ cachedChannel,
72+ accountId,
73+}),
74+ runtimeSnapshot,
75+})
76+) {
77+return true;
78+}
79+}
80+}
81+82+return false;
83+}
84+1085export const healthHandlers: GatewayRequestHandlers = {
1186health: async ({ respond, context, params, client }) => {
1287const { getHealthCache, refreshHealthSnapshot, logHealth } = context;
@@ -15,7 +90,23 @@ export const healthHandlers: GatewayRequestHandlers = {
1590const includeSensitive = scopes.includes(ADMIN_SCOPE);
1691const now = Date.now();
1792const cached = getHealthCache();
18-if (!wantsProbe && cached && now - cached.ts < HEALTH_REFRESH_INTERVAL_MS) {
93+let cachedDiffersFromRuntime = false;
94+if (!wantsProbe && cached) {
95+try {
96+cachedDiffersFromRuntime = cachedHealthDiffersFromRuntime(
97+cached,
98+context.getRuntimeSnapshot(),
99+);
100+} catch {
101+cachedDiffersFromRuntime = false;
102+}
103+}
104+if (
105+!wantsProbe &&
106+cached &&
107+!cachedDiffersFromRuntime &&
108+now - cached.ts < HEALTH_REFRESH_INTERVAL_MS
109+) {
19110respond(true, cached, undefined, { cached: true });
20111void refreshHealthSnapshot({ probe: false, includeSensitive }).catch((err) =>
21112logHealth.error(`background health refresh failed: ${formatError(err)}`),
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。