Skip to content

Commit 9c58ad6

Browse files
IM.codesclaude
andcommitted
fix(push): use dedicated dispatcher for relay to avoid stale keep-alive sockets
Cloudflare edge half-closes idle keep-alive sockets, leaving undici's pool with stale connections that produce a persistent 502 storm until the process restarts. Use a dedicated Agent with short keepAliveTimeout (4s) so bad sockets are evicted quickly and the next request opens a fresh connection. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 3a0ca77 commit 9c58ad6

File tree

1 file changed

+15
-0
lines changed

1 file changed

+15
-0
lines changed

server/src/routes/push.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,25 @@
77
* Android: FCM legacy HTTP API
88
*/
99
import { Hono } from 'hono';
10+
import { Agent } from 'undici';
1011
import type { Env } from '../env.js';
1112
import type { Database } from '../db/client.js';
1213
import { requireAuth } from '../security/authorization.js';
1314
import { SignJWT, importPKCS8 } from 'jose';
1415
import logger from '../util/logger.js';
1516

17+
// Dedicated dispatcher for relay calls. Cloudflare edge sometimes silently
18+
// half-closes long-lived keep-alive sockets, leaving undici's pool with stale
19+
// connections that produce a persistent 502 storm until the process restarts.
20+
// Short keepAliveTimeout + small max connections forces fresh sockets often
21+
// enough to recover automatically.
22+
const relayDispatcher = new Agent({
23+
keepAliveTimeout: 4_000,
24+
keepAliveMaxTimeout: 10_000,
25+
connections: 8,
26+
pipelining: 0,
27+
});
28+
1629
export const pushRoutes = new Hono<{ Bindings: Env; Variables: { userId: string; role: string } }>();
1730

1831
// Auth required for register/unregister but NOT for relay (self-hosted servers call relay without auth)
@@ -184,6 +197,8 @@ async function relayPush(relayBaseUrl: string, token: string, platform: string,
184197
data: payload.data,
185198
}),
186199
signal: AbortSignal.timeout(10_000),
200+
// @ts-expect-error undici dispatcher option, not in standard fetch types
201+
dispatcher: relayDispatcher,
187202
});
188203

189204
if (!res.ok) {

0 commit comments

Comments
 (0)