perf: avoid opening bootstrap transcripts on cache hits

readRecentBootstrapTranscriptOutcome opened the session file and stat'd it
through the handle BEFORE checking the per-(file,mtime,size) outcome cache, so
every cache hit still paid a full open()+close(). During a tracked launch the
per-member lookup cache is bypassed, so the project-dir scan re-runs every poll
across every recent session file x every member, turning each cache hit into a
wasted open() syscall. The native sample of a 6-member mixed launch put __open
at ~51-54k; correcting the earlier attribution, this open-before-cache-check
(NOT the watcher rebuilds addressed in the previous commit) is the dominant
source -- removing the per-rebuild watcher churn left __open essentially flat.

Stat the path with fs.promises.stat (no fd) for the cache check and return the
cached outcome without opening. getParsedBootstrapTranscriptTail now opens the
file itself, lazily, only when its shared parse cache also misses (the file
genuinely changed since last parse), so a hit on either the per-member outcome
cache or the shared tail-parse cache avoids the open entirely. The tail read is
wrapped in try/finally to close the handle. Parsing/scan logic is byte-for-byte
unchanged; only the redundant open is removed. Bootstrap-transcript tests pass.
This commit is contained in:
777genius 2026-05-30 12:23:48 +03:00
parent 8b3cec8013
commit ccea3e015d

View file

@ -30323,7 +30323,6 @@ export class TeamProvisioningService {
contextMemberNames?: readonly string[];
} = {}
): Promise<BootstrapTranscriptOutcome | null> {
let handle: fs.promises.FileHandle | null = null;
const normalizedMemberName = memberName.trim().toLowerCase();
const contextMemberNames = Array.from(
new Set(
@ -30341,8 +30340,11 @@ export class TeamProvisioningService {
contextMemberNames,
});
try {
handle = await fs.promises.open(filePath, 'r');
const stat = await handle.stat();
// Stat without opening: on a cache hit we must NOT open the file. During a
// tracked launch the per-member lookup cache is bypassed, so this scan runs
// for every recent session file x every member x every poll; opening before
// the cache check turned every one of those into a wasted open() syscall.
const stat = await fs.promises.stat(filePath);
if (!stat.isFile() || stat.size <= 0) {
return null;
}
@ -30351,9 +30353,9 @@ export class TeamProvisioningService {
return cached.outcome;
}
// Parse the transcript tail once per (filePath, mtime, size) and share it
// across members. The per-member filter/scan below is byte-for-byte the same
// logic as before; only the redundant read + JSON.parse is now memoized.
const parsedLines = await this.getParsedBootstrapTranscriptTail(handle, filePath, stat);
// across members. getParsedBootstrapTranscriptTail opens the file ITSELF only
// when its parse cache misses, so a shared-cache hit also avoids the open.
const parsedLines = await this.getParsedBootstrapTranscriptTail(filePath, stat);
const shouldCollectBootstrapContext = options.allowAnonymousFailure !== true;
const bootstrapContextMembers = new Set<string>();
const candidates: BootstrapTranscriptOutcomeCandidate[] = [];
@ -30433,11 +30435,7 @@ export class TeamProvisioningService {
return outcome;
} catch {
return null;
} finally {
await handle?.close().catch(() => undefined);
}
return null;
}
private buildBootstrapTranscriptOutcomeCacheKey(input: {
@ -30464,7 +30462,6 @@ export class TeamProvisioningService {
}
private async getParsedBootstrapTranscriptTail(
handle: fs.promises.FileHandle,
filePath: string,
stat: { mtimeMs: number; size: number }
): Promise<ParsedBootstrapTranscriptTailLine[]> {
@ -30476,9 +30473,17 @@ export class TeamProvisioningService {
const start = Math.max(0, stat.size - TeamProvisioningService.BOOTSTRAP_FAILURE_TAIL_BYTES);
const length = stat.size - start;
if (length > 0) {
const buffer = Buffer.alloc(length);
await handle.read(buffer, 0, length, start);
const rawLines = buffer.toString('utf8').split('\n');
// Open lazily: only a genuine parse-cache miss (file changed since last
// parse) reaches here, so we never open a file whose tail is already cached.
const handle = await fs.promises.open(filePath, 'r');
let rawLines: string[];
try {
const buffer = Buffer.alloc(length);
await handle.read(buffer, 0, length, start);
rawLines = buffer.toString('utf8').split('\n');
} finally {
await handle.close().catch(() => undefined);
}
if (start > 0) {
rawLines.shift();
}