mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-07-10 01:29:17 +00:00
#3656 fixed the read side of glued '}{' JSONL records — when a process was killed mid-appendFile, the trailing '\n' was lost and the next record was concatenated. The write side was left for a follow-up (#3681). This adds flush:true (fsync) to every per-line append: - jsonl-utils.ts writeLine / writeLineSync (session transcripts, auto-titles, prompt history) - debugLogger.ts appendFile (per-session debug log) jsonl-utils.ts write() (full-file replace) now goes through atomicWriteFileSync so a crash during overwrite cannot corrupt the session transcript either. Trade-off: fsync on every append adds disk-sync latency (single-digit ms on SSD, more on spinning disk / network FS). Acceptable for a few writes per turn; the alternative is silently losing the last record of every interrupted session, which #3681 explicitly flagged. Refs: #4095 Phase 2 Tier 3b Closes: #3681
This commit is contained in:
parent
2a6ded1fd0
commit
b7badc7775
3 changed files with 19 additions and 11 deletions
|
|
@ -96,7 +96,7 @@ describe('debugLogger', () => {
|
|||
expect(fs.appendFile).toHaveBeenCalledWith(
|
||||
Storage.getDebugLogPath('test-session-123'),
|
||||
'2026-01-24T10:30:00.000Z [DEBUG] Hello world\n',
|
||||
'utf8',
|
||||
{ encoding: 'utf8', flush: true },
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -109,7 +109,7 @@ describe('debugLogger', () => {
|
|||
expect(fs.appendFile).toHaveBeenCalledWith(
|
||||
Storage.getDebugLogPath('test-session-123'),
|
||||
'2026-01-24T10:30:00.000Z [INFO] [STARTUP] Server started\n',
|
||||
'utf8',
|
||||
{ encoding: 'utf8', flush: true },
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -149,7 +149,7 @@ describe('debugLogger', () => {
|
|||
expect.stringContaining(
|
||||
'[trace_id=realtraceidddddddddddddddddddddd span_id=realspanid111111]',
|
||||
),
|
||||
'utf8',
|
||||
{ encoding: 'utf8', flush: true },
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -170,7 +170,7 @@ describe('debugLogger', () => {
|
|||
expect(fs.appendFile).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
expect.not.stringContaining('trace_id='),
|
||||
'utf8',
|
||||
{ encoding: 'utf8', flush: true },
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -187,7 +187,7 @@ describe('debugLogger', () => {
|
|||
expect(fs.appendFile).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
expect.not.stringContaining('trace_id='),
|
||||
'utf8',
|
||||
{ encoding: 'utf8', flush: true },
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -230,7 +230,7 @@ describe('debugLogger', () => {
|
|||
expect.stringContaining(
|
||||
'[trace_id=cccccccccccccccccccccccccccccccc span_id=dddddddddddddddd]',
|
||||
),
|
||||
'utf8',
|
||||
{ encoding: 'utf8', flush: true },
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -264,7 +264,7 @@ describe('debugLogger', () => {
|
|||
expect(fs.appendFile).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
expect.stringContaining('Count: 42 items'),
|
||||
'utf8',
|
||||
{ encoding: 'utf8', flush: true },
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -148,7 +148,9 @@ function writeLog(
|
|||
const line = buildLogLine(level, message, tag, traceCtx);
|
||||
|
||||
void ensureDebugDirExists()
|
||||
.then(() => fs.appendFile(logFilePath, line, 'utf8'))
|
||||
.then(() =>
|
||||
fs.appendFile(logFilePath, line, { encoding: 'utf8', flush: true }),
|
||||
)
|
||||
.catch(() => {
|
||||
hasWriteFailure = true;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { atomicWriteFileSync } from './atomicFileWrite.js';
|
||||
import readline from 'node:readline';
|
||||
import { finished } from 'node:stream/promises';
|
||||
import { Mutex } from 'async-mutex';
|
||||
|
|
@ -267,7 +268,12 @@ export async function writeLine(
|
|||
await fs.promises.mkdir(dir, { recursive: true });
|
||||
ensuredDirs.add(dir);
|
||||
}
|
||||
await fs.promises.appendFile(filePath, line, 'utf8');
|
||||
// flush:true fsyncs after each line so a process killed mid-write
|
||||
// doesn't leave a glued `}{` record on disk (closes #3681).
|
||||
await fs.promises.appendFile(filePath, line, {
|
||||
encoding: 'utf8',
|
||||
flush: true,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -282,7 +288,7 @@ export function writeLineSync(filePath: string, data: unknown): void {
|
|||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
fs.appendFileSync(filePath, line, 'utf8');
|
||||
fs.appendFileSync(filePath, line, { encoding: 'utf8', flush: true });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -296,7 +302,7 @@ export function write(filePath: string, data: unknown[]): void {
|
|||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
fs.writeFileSync(filePath, `${lines}\n`, 'utf8');
|
||||
atomicWriteFileSync(filePath, `${lines}\n`, { encoding: 'utf8' });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue