| @@ -37,6 +37,8 @@ const sidecarLog = log.scope("sidecar"); · 36 unmodified lines |
| 37 | const STDERR_TAIL_LIMIT = 8 * 1024; | 37 | const STDERR_TAIL_LIMIT = 8 * 1024; |
| 38 | const READY_SENTINEL = "EXECUTOR_READY"; | 38 | const READY_SENTINEL = "EXECUTOR_READY"; |
| 39 | const ATTACHED_SENTINEL = "EXECUTOR_ATTACHED"; | 39 | const ATTACHED_SENTINEL = "EXECUTOR_ATTACHED"; |
| | 40 | const ATTACH_OWNER_TIMEOUT_MS = 15_000; |
| | 41 | const ATTACH_OWNER_POLL_MS = 150; |
| 40 | | 42 | |
| 41 | // Children deliberately stopped via stopSidecar (quit, restart, update) — | 43 | // Children deliberately stopped via stopSidecar (quit, restart, update) — |
| 42 | // their exits are expected and must not be reported as crashes. | 44 | // their exits are expected and must not be reported as crashes. |
| @@ -281,6 +283,7 @@ export async function startSidecar(options: StartOptions = {}): Promise<SidecarC · 238 unmodified lines |
| 281 | let stdoutControlBuffer = ""; | 283 | let stdoutControlBuffer = ""; |
| 282 | let resolved = false; | 284 | let resolved = false; |
| 283 | let rejected = false; | 285 | let rejected = false; |
| | 286 | let attachedOwnerResolution: Promise<boolean> | null = null; |
| 284 | | 287 | |
| 285 | const logStdoutLine = makeLineSplitter((line) => sidecarLog.info(line)); | 288 | const logStdoutLine = makeLineSplitter((line) => sidecarLog.info(line)); |
| 286 | const logStderrLine = makeLineSplitter((line) => sidecarLog.error(line)); | 289 | const logStderrLine = makeLineSplitter((line) => sidecarLog.error(line)); |
| @@ -294,12 +297,21 @@ export async function startSidecar(options: StartOptions = {}): Promise<SidecarC · 7 unmodified lines |
| 294 | | 297 | |
| 295 | const resolveAttachedOwner = async (): Promise<boolean> => { | 298 | const resolveAttachedOwner = async (): Promise<boolean> => { |
| 296 | if (resolved || rejected) return true; | 299 | if (resolved || rejected) return true; |
| 297 | const attached = await attachToReachableLocalServer(dataDir); | 300 | if (!attachedOwnerResolution) { |
| 298 | if (!attached) return false; | 301 | attachedOwnerResolution = waitForReachableLocalServer(dataDir) |
| 299 | resolved = true; | 302 | .then((attached) => { |
| 300 | expectedExits.add(child); | 303 | if (resolved || rejected) return true; |
| 301 | resolveStart(attached); | 304 | if (!attached) return false; |
| 302 | return true; | 305 | resolved = true; |
| | 306 | expectedExits.add(child); |
| | 307 | resolveStart(attached); |
| | 308 | return true; |
| | 309 | }) |
| | 310 | .finally(() => { |
| | 311 | attachedOwnerResolution = null; |
| | 312 | }); |
| | 313 | } |
| | 314 | return attachedOwnerResolution; |
| 303 | }; | 315 | }; |
| 304 | | 316 | |
| 305 | const onStdout = (chunk: Buffer) => { | 317 | const onStdout = (chunk: Buffer) => { |
| @@ -418,6 +430,21 @@ const isDaemonReachable = async (origin: string): Promise<boolean> => { · 112 unmodified lines |
| 418 | return false; | 430 | return false; |
| 419 | }; | 431 | }; |
| 420 | | 432 | |
| | 433 | const waitForReachableLocalServer = async ( |
| | 434 | dataDir: string, |
| | 435 | options: { readonly requireCliDaemon?: boolean } = {}, |
| | 436 | ): Promise<SidecarConnection | null> => { |
| | 437 | const deadline = Date.now() + ATTACH_OWNER_TIMEOUT_MS; |
| | 438 | while (Date.now() <= deadline) { |
| | 439 | const attached = await attachToReachableLocalServer(dataDir, options); |
| | 440 | if (attached) return attached; |
| | 441 | const remaining = deadline - Date.now(); |
| | 442 | if (remaining <= 0) return null; |
| | 443 | await delay(Math.min(ATTACH_OWNER_POLL_MS, remaining)); |
| | 444 | } |
| | 445 | return null; |
| | 446 | }; |
| | 447 | |
| 421 | const attachToReachableLocalServer = async ( | 448 | const attachToReachableLocalServer = async ( |
| 422 | dataDir: string, | 449 | dataDir: string, |
| 423 | options: { readonly requireCliDaemon?: boolean } = {}, | 450 | options: { readonly requireCliDaemon?: boolean } = {}, |