Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 2025-05-15 - [Sequential session destruction in SDKs]
**Learning:** All Copilot SDKs (Node.js, Python, Go, .NET) were initially implementing session destruction sequentially during client shutdown. This leads to a linear increase in shutdown time as the number of active sessions grows, especially when individual destructions involve retries and backoff.
**Action:** Parallelize session cleanup using language-specific concurrency primitives (e.g., `Promise.all` in Node.js, `asyncio.gather` in Python, `Task.WhenAll` in .NET, or WaitGroups/Channels in Go) to ensure shutdown time remains constant and minimal.
18 changes: 12 additions & 6 deletions nodejs/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,8 @@ export class CopilotClient {
async stop(): Promise<Error[]> {
const errors: Error[] = [];

// Destroy all active sessions with retry logic
for (const session of this.sessions.values()) {
// Destroy all active sessions in parallel with retry logic
const sessionPromises = Array.from(this.sessions.values()).map(async (session) => {
const sessionId = session.sessionId;
let lastError: Error | null = null;
Comment on lines +301 to 304
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Array.from(this.sessions.values()) snapshots the sessions at the start of stop(). If a session is created/resumed while stop() is running, it won’t be destroyed but will still be removed by this.sessions.clear(), which can skip session.destroy() cleanup (and differs from the previous live Map iteration behavior). Consider blocking session creation while stopping (e.g., a stopping flag/state checked in createSession/resumeSession) or draining the map as you schedule destructions so sessions added during shutdown are either rejected or deterministically cleaned up.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback


Expand All @@ -321,12 +321,18 @@ export class CopilotClient {
}

if (lastError) {
errors.push(
new Error(
`Failed to destroy session ${sessionId} after 3 attempts: ${lastError.message}`
)
return new Error(
`Failed to destroy session ${sessionId} after 3 attempts: ${lastError.message}`
);
}
return null;
});

const results = await Promise.all(sessionPromises);
for (const result of results) {
if (result instanceof Error) {
errors.push(result);
}
}
this.sessions.clear();

Expand Down
Loading