perf(lbug): skip per-row microtask tick in BufferedCSVWriter (#2203 U3)

addRow awaited an already-resolved promise on every buffered row, scheduling
a microtask per node even when nothing flushed (millions at scale). It now
returns a promise ONLY when it flushes; the node-emit loop awaits once per
iteration after the switch. Flush/drain semantics are unchanged, so
backpressure on the rows that actually write is preserved and the emitted
CSV bytes are byte-identical (covered by the determinism + differential tests).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Gergo Magyar 2026-06-15 16:20:56 +00:00
parent 2e5ab2be30
commit 692ff86a31

View file

@ -183,13 +183,20 @@ class BufferedCSVWriter {
this.buffer.push(header);
}
addRow(row: string) {
/**
* Buffer a row. Returns a promise ONLY when the buffer crossed FLUSH_EVERY
* and a disk write was issued; otherwise returns `undefined` so the caller
* can skip awaiting (#2203 U3) — avoiding a microtask tick on every buffered
* row (millions at scale). The flush promise still resolves on drain, so
* backpressure is preserved on the rows that actually write.
*/
addRow(row: string): Promise<void> | undefined {
this.buffer.push(row);
this.rows++;
if (this.buffer.length >= FLUSH_EVERY) {
return this.flush();
}
return Promise.resolve();
return undefined;
}
flush(): Promise<void> {
@ -388,10 +395,14 @@ export const streamAllCSVsToDisk = async (
if (seenNodeIds.has(node.id)) continue;
seenNodeIds.add(node.id);
// addRow returns a promise only when it flushes; awaiting it once after the
// switch (instead of `await`-ing every addRow) skips a per-row microtask
// tick on the ~FLUSH_EVERY-1 buffered rows between flushes (#2203 U3).
let pending: Promise<void> | undefined;
switch (node.label) {
case 'File': {
const content = await extractContent(node, contentCache);
await fileWriter.addRow(
pending = fileWriter.addRow(
[
escapeCSVField(node.id),
escapeCSVField(node.properties.name || ''),
@ -402,7 +413,7 @@ export const streamAllCSVsToDisk = async (
break;
}
case 'Folder':
await folderWriter.addRow(
pending = folderWriter.addRow(
[
escapeCSVField(node.id),
escapeCSVField(node.properties.name || ''),
@ -413,7 +424,7 @@ export const streamAllCSVsToDisk = async (
case 'Community': {
const keywords = node.properties.keywords || [];
const keywordsStr = `[${keywords.map((k: string) => `'${k.replace(/\\/g, '\\\\').replace(/'/g, "''").replace(/,/g, '\\,')}'`).join(',')}]`;
await communityWriter.addRow(
pending = communityWriter.addRow(
[
escapeCSVField(node.id),
escapeCSVField(node.properties.name || ''),
@ -430,7 +441,7 @@ export const streamAllCSVsToDisk = async (
case 'Process': {
const communities = node.properties.communities || [];
const communitiesStr = `[${communities.map((c: string) => `'${c.replace(/'/g, "''")}'`).join(',')}]`;
await processWriter.addRow(
pending = processWriter.addRow(
[
escapeCSVField(node.id),
escapeCSVField(node.properties.name || ''),
@ -446,7 +457,7 @@ export const streamAllCSVsToDisk = async (
}
case 'Method': {
const content = await extractContent(node, contentCache);
await methodWriter.addRow(
pending = methodWriter.addRow(
[
escapeCSVField(node.id),
escapeCSVField(node.properties.name || ''),
@ -464,7 +475,7 @@ export const streamAllCSVsToDisk = async (
}
case 'Section': {
const content = await extractContent(node, contentCache);
await sectionWriter.addRow(
pending = sectionWriter.addRow(
[
escapeCSVField(node.id),
escapeCSVField(node.properties.name || ''),
@ -487,7 +498,7 @@ export const streamAllCSVsToDisk = async (
const errorKeysStr = `[${errorKeys.map((k: string) => `'${k.replace(/'/g, "''")}'`).join(',')}]`;
const middleware = node.properties.middleware || [];
const middlewareStr = `[${middleware.map((m: string) => `'${m.replace(/'/g, "''")}'`).join(',')}]`;
await routeWriter.addRow(
pending = routeWriter.addRow(
[
escapeCSVField(node.id),
escapeCSVField(node.properties.name || ''),
@ -500,7 +511,7 @@ export const streamAllCSVsToDisk = async (
break;
}
case 'Tool':
await toolWriter.addRow(
pending = toolWriter.addRow(
[
escapeCSVField(node.id),
escapeCSVField(node.properties.name || ''),
@ -510,7 +521,7 @@ export const streamAllCSVsToDisk = async (
);
break;
case 'BasicBlock':
await basicBlockWriter.addRow(
pending = basicBlockWriter.addRow(
[
escapeCSVField(node.id),
escapeCSVField(node.properties.filePath || ''),
@ -525,7 +536,7 @@ export const streamAllCSVsToDisk = async (
const writer = codeWriterMap[node.label];
if (writer) {
const content = await extractContent(node, contentCache);
await writer.addRow(
pending = writer.addRow(
[
escapeCSVField(node.id),
escapeCSVField(node.properties.name || ''),
@ -542,7 +553,7 @@ export const streamAllCSVsToDisk = async (
const mlWriter = multiLangWriters.get(node.label);
if (mlWriter) {
const content = await extractContent(node, contentCache);
await mlWriter.addRow(
pending = mlWriter.addRow(
[
escapeCSVField(node.id),
escapeCSVField(node.properties.name || ''),
@ -561,6 +572,7 @@ export const streamAllCSVsToDisk = async (
break;
}
}
if (pending) await pending;
}
// Finish all node writers