Skip to content

Commit 25da74e

Browse files
committed
wip speed up delete
1 parent 63ae91b commit 25da74e

1 file changed

Lines changed: 96 additions & 16 deletions

File tree

apps/cloud/src/mcp-session.ts

Lines changed: 96 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ const resolveSessionMeta = Effect.fn("McpSessionDO.resolveSessionMeta")(function
182182
// ---------------------------------------------------------------------------
183183

184184
export class McpSessionDO extends DurableObject {
185+
private readonly instanceCreatedAt = Date.now();
185186
private mcpServer: McpServer | null = null;
186187
private transport: WorkerTransport | null = null;
187188
private initialized = false;
@@ -223,6 +224,17 @@ export class McpSessionDO extends DurableObject {
223224
await this.ctx.storage.put(SESSION_META_KEY, sessionMeta);
224225
}
225226

227+
private entryAttrs(methodEnteredAt: number): Record<string, unknown> {
228+
const now = Date.now();
229+
return {
230+
"mcp.do.instance_age_ms": now - this.instanceCreatedAt,
231+
"mcp.do.method_entry_delay_ms": now - methodEnteredAt,
232+
"mcp.session.initialized": this.initialized,
233+
"mcp.session.has_transport": !!this.transport,
234+
"mcp.session.has_meta_memory": !!this.sessionMeta,
235+
};
236+
}
237+
226238
private clearSessionState(): Effect.Effect<void> {
227239
return Effect.promise(async () => {
228240
this.sessionMeta = null;
@@ -274,6 +286,68 @@ export class McpSessionDO extends DurableObject {
274286
);
275287
}
276288

289+
private closeRuntime(): Effect.Effect<void> {
290+
const self = this;
291+
return Effect.promise(async () => {
292+
if (self.transport) {
293+
await self.transport.close().catch(() => undefined);
294+
self.transport = null;
295+
}
296+
if (self.mcpServer) {
297+
await self.mcpServer.close().catch(() => undefined);
298+
self.mcpServer = null;
299+
}
300+
if (self.dbHandle) {
301+
await self.dbHandle.end();
302+
self.dbHandle = null;
303+
}
304+
self.initialized = false;
305+
}).pipe(Effect.orDie);
306+
}
307+
308+
private restoreRuntimeFromStorage(
309+
request: Request,
310+
): Effect.Effect<"restored" | "missing_meta"> {
311+
const self = this;
312+
return Effect.gen(function* () {
313+
if (self.initialized && self.transport) return "restored" as const;
314+
315+
const sessionMeta = yield* self.loadSessionMeta();
316+
if (!sessionMeta) {
317+
yield* Effect.annotateCurrentSpan({
318+
"mcp.session.restore.outcome": "missing_meta",
319+
});
320+
return "missing_meta" as const;
321+
}
322+
323+
yield* self.closeRuntime();
324+
self.dbHandle = makeLongLivedDb();
325+
const runtime = yield* self.createConnectedRuntime(sessionMeta, {
326+
dbHandle: self.dbHandle,
327+
enableJsonResponse: request.method !== "GET",
328+
});
329+
self.mcpServer = runtime.mcpServer;
330+
self.transport = runtime.transport;
331+
self.initialized = true;
332+
self.lastActivityMs = Date.now();
333+
yield* Effect.promise(() => self.ctx.storage.setAlarm(Date.now() + HEARTBEAT_MS)).pipe(
334+
Effect.withSpan("McpSessionDO.setAlarm"),
335+
);
336+
yield* Effect.annotateCurrentSpan({
337+
"mcp.session.restore.outcome": "restored",
338+
});
339+
return "restored" as const;
340+
}).pipe(
341+
Effect.withSpan("McpSessionDO.restoreRuntime", {
342+
attributes: {
343+
"mcp.request.method": request.method,
344+
"mcp.request.session_id_present": !!request.headers.get("mcp-session-id"),
345+
},
346+
}),
347+
Effect.orDie,
348+
);
349+
}
350+
277351
private resolveAndStoreSessionMeta(token: McpSessionInit) {
278352
const self = this;
279353
return Effect.gen(function* () {
@@ -294,9 +368,14 @@ export class McpSessionDO extends DurableObject {
294368
}
295369

296370
async init(token: McpSessionInit, incoming?: IncomingTraceHeaders): Promise<void> {
371+
const methodEnteredAt = Date.now();
297372
if (this.initialized) return;
373+
const self = this;
298374
return Effect.runPromise(
299-
this.doInit(token).pipe(
375+
Effect.gen(function* () {
376+
yield* Effect.annotateCurrentSpan(self.entryAttrs(methodEnteredAt));
377+
yield* self.doInit(token);
378+
}).pipe(
300379
Effect.withSpan("McpSessionDO.init", {
301380
attributes: { "mcp.auth.organization_id": token.organizationId },
302381
}),
@@ -356,6 +435,7 @@ export class McpSessionDO extends DurableObject {
356435
}
357436

358437
async handleRequest(request: Request): Promise<Response> {
438+
const methodEnteredAt = Date.now();
359439
// Wrap the dispatch in an Effect span so every DO request — not just
360440
// the rare new-session `init()` — shows up in Axiom. Basic attributes
361441
// only (method, session-id presence, response status); rich client
@@ -368,6 +448,7 @@ export class McpSessionDO extends DurableObject {
368448
} satisfies IncomingTraceHeaders;
369449
const self = this;
370450
const program = Effect.gen(function* () {
451+
yield* Effect.annotateCurrentSpan(self.entryAttrs(methodEnteredAt));
371452
// Capture the request-entry span so the host-mcp `parentSpan` getter
372453
// — fired by deferred MCP SDK callbacks after this Effect has already
373454
// returned — anchors engine spans under the same trace. Cleared in a
@@ -403,9 +484,19 @@ export class McpSessionDO extends DurableObject {
403484

404485
private dispatchRequest(request: Request): Effect.Effect<Response> {
405486
if (!this.initialized || !this.transport) {
406-
return Effect.succeed(
407-
jsonRpcError(404, -32001, "Session timed out due to inactivity — please reconnect"),
408-
);
487+
if (request.method === "DELETE") {
488+
return this.clearSessionState().pipe(
489+
Effect.as(new Response(null, { status: 204 })),
490+
Effect.withSpan("mcp.session.stale_delete"),
491+
);
492+
}
493+
return Effect.gen(this, function* () {
494+
const restored = yield* this.restoreRuntimeFromStorage(request);
495+
if (restored === "restored") {
496+
return yield* this.dispatchRequest(request);
497+
}
498+
return jsonRpcError(404, -32001, "Session timed out due to inactivity — please reconnect");
499+
});
409500
}
410501

411502
this.lastActivityMs = Date.now();
@@ -461,18 +552,7 @@ export class McpSessionDO extends DurableObject {
461552
}
462553

463554
private async cleanup(): Promise<void> {
464-
if (this.transport) {
465-
await this.transport.close().catch(() => undefined);
466-
this.transport = null;
467-
}
468-
if (this.mcpServer) {
469-
await this.mcpServer.close().catch(() => undefined);
470-
this.mcpServer = null;
471-
}
472-
if (this.dbHandle) {
473-
await this.dbHandle.end();
474-
this.dbHandle = null;
475-
}
555+
await Effect.runPromise(this.closeRuntime());
476556
await Effect.runPromise(this.clearSessionState());
477557
}
478558
}

0 commit comments

Comments
 (0)