fix: harden daemon and large-index recovery paths (#1562)
* fix: harden indexing recovery and daemon liveness * test: cover daemon and recovery review gaps * test: pin that a failure marker never blocks a later successful parse (#1557 retry-discard guard) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: danusha2345 <ewidusoc498@gmail.com> Co-authored-by: Colby McHenry <me@colbymchenry.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
danusha2345
Colby McHenry
parent
d8f2eeaddf
commit
81e1f4a92f
@@ -29,6 +29,7 @@
|
||||
*/
|
||||
|
||||
import * as crypto from 'crypto';
|
||||
import * as net from 'net';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { getCodeGraphDir } from '../directory';
|
||||
@@ -101,6 +102,55 @@ export interface DaemonLockInfo {
|
||||
startedAt: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the process named by a lockfile is the CodeGraph daemon serving
|
||||
* its socket. A bare PID liveness probe is insufficient because OSes reuse PIDs
|
||||
* after an OOM/SIGKILL (#1553).
|
||||
*/
|
||||
export function probeDaemonIdentity(info: DaemonLockInfo, timeoutMs = 1_000): Promise<boolean> {
|
||||
if (!Number.isInteger(info.pid) || info.pid <= 0 || !info.socketPath) return Promise.resolve(false);
|
||||
return new Promise<boolean>((resolve) => {
|
||||
let socket: net.Socket;
|
||||
let buffer = '';
|
||||
let done = false;
|
||||
const finish = (ok: boolean) => {
|
||||
if (done) return;
|
||||
done = true;
|
||||
clearTimeout(timer);
|
||||
socket.destroy();
|
||||
resolve(ok);
|
||||
};
|
||||
const timer = setTimeout(() => finish(false), timeoutMs);
|
||||
timer.unref?.();
|
||||
try {
|
||||
socket = net.createConnection(info.socketPath);
|
||||
} catch {
|
||||
clearTimeout(timer);
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
socket.setEncoding('utf8');
|
||||
socket.on('data', (chunk) => {
|
||||
buffer += String(chunk);
|
||||
if (buffer.length > 4096) return finish(false);
|
||||
const newline = buffer.indexOf('\n');
|
||||
if (newline < 0) return;
|
||||
try {
|
||||
const hello = JSON.parse(buffer.slice(0, newline)) as Record<string, unknown>;
|
||||
finish(
|
||||
hello.protocol === 1 &&
|
||||
hello.pid === info.pid &&
|
||||
(info.version === 'unknown' || hello.codegraph === info.version)
|
||||
);
|
||||
} catch {
|
||||
finish(false);
|
||||
}
|
||||
});
|
||||
socket.on('error', () => finish(false));
|
||||
socket.on('close', () => finish(false));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize a {@link DaemonLockInfo} for writing to the pidfile. JSON for
|
||||
* human readability — operators occasionally `cat` this when debugging.
|
||||
|
||||
Reference in New Issue
Block a user