Skip to content

Commit c367d00

Browse files
committed
perf(session): stop re-hashing unchanged segments on each commit
Steady-state checkpoints stage the writer's modified paths instead of every contiguous segment, and the empty-commit check no longer walks every tool-output blob once a root file is already dirty. Rewrites and the first commit of a store still stage the full segment set, and a clean tree still refuses a no-op commit.
1 parent f5e3cd2 commit c367d00

2 files changed

Lines changed: 289 additions & 32 deletions

File tree

‎src/session/optimized-context-store.test.ts‎

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,99 @@ describe("createOptimizedContextStore checkpoint", () => {
685685
expect(tree).not.toContain("untracked-junk.txt");
686686
});
687687

688+
test("a later checkpoint does not re-read an unchanged turn segment", async () => {
689+
const dir = tempDir();
690+
const store = await createOptimizedContextStore(dir);
691+
const chunk = "s".repeat(100_000);
692+
const turns: ConversationTurn[] = [];
693+
const middle = segmentFileName(TURNS_FILE, 1);
694+
const tail = segmentFileName(TURNS_FILE, 2);
695+
do {
696+
turns.push(turn(`${turns.length}-${chunk}`));
697+
await store.writeTurns(turns);
698+
} while (!fs.existsSync(path.join(dir, tail)) && turns.length < 20);
699+
expect(fs.existsSync(path.join(dir, middle))).toBe(true);
700+
expect(fs.existsSync(path.join(dir, tail))).toBe(true);
701+
await store.writeMetadata(EMPTY_CHECKPOINT_METADATA);
702+
await store.commit({ message: "three segments" });
703+
704+
turns.push(turn("tail"));
705+
await store.writeTurns(turns);
706+
const reads = await countPathOps("readFile", middle, () =>
707+
store.commit({ message: "append tail" }),
708+
);
709+
expect(reads).toBe(0);
710+
});
711+
712+
test("a turn commit does not lstat every seeded tool-output blob", async () => {
713+
const dir = tempDir();
714+
const store = await createOptimizedContextStore(dir);
715+
const blobCount = 24;
716+
for (let index = 0; index < blobCount; index++) {
717+
await store.writeBlob(
718+
`blob-${index}`,
719+
new TextEncoder().encode(`body-${index}`),
720+
"text/plain",
721+
);
722+
}
723+
const turns = [turn("seed")];
724+
await store.writeTurns(turns);
725+
await store.writeMetadata(EMPTY_CHECKPOINT_METADATA);
726+
await store.commit({ message: "seed blobs" });
727+
728+
const lstats = await countPathOps(
729+
"lstat",
730+
`${path.sep}tool-output${path.sep}`,
731+
async () => {
732+
turns.push(turn("next"));
733+
await store.writeTurns(turns);
734+
await store.commit({ message: "next turn" });
735+
},
736+
);
737+
// Vendor commit still stats each blob once while staging it. The
738+
// empty-commit matrix must not stat them again on a turn that already
739+
// changed a managed file.
740+
expect(lstats).toBeLessThan(blobCount + blobCount / 2);
741+
});
742+
743+
test("seeded tool-output does not commit a no-op and a fresh store still heals blob bytes", async () => {
744+
const dir = tempDir();
745+
const store = await createOptimizedContextStore(dir);
746+
await store.writeBlob(
747+
"kept",
748+
new TextEncoder().encode("original"),
749+
"text/plain",
750+
);
751+
await store.writeTurns([turn("seed")]);
752+
await store.writeMetadata(EMPTY_CHECKPOINT_METADATA);
753+
const first = await store.commit({ message: "seed blob" });
754+
755+
fs.writeFileSync(path.join(dir, "partial.jsonl"), '{"reason":"abort"}\n');
756+
const second = await store.commit({ message: "empty with blobs" });
757+
expect(second.hash).toBe(first.hash);
758+
759+
const blobName = fs
760+
.readdirSync(path.join(dir, "tool-output"))
761+
.find((name) => name.startsWith("kept"));
762+
if (blobName === undefined) throw new Error("seeded blob missing");
763+
fs.writeFileSync(path.join(dir, "tool-output", blobName), "rewritten");
764+
fs.writeFileSync(path.join(dir, "tool-output", "stray.txt"), "stray");
765+
766+
const crashed = await createOptimizedContextStore(dir);
767+
const healed = await crashed.commit({ message: "heal blobs" });
768+
expect(healed.hash).not.toBe(first.hash);
769+
const tree = await gitLsTree(dir);
770+
expect(tree).toContain(`tool-output/${blobName}`);
771+
expect(tree).toContain("tool-output/stray.txt");
772+
const { blob } = await git.readBlob({
773+
fs,
774+
dir,
775+
oid: healed.hash,
776+
filepath: `tool-output/${blobName}`,
777+
});
778+
expect(new TextDecoder().decode(blob)).toBe("rewritten");
779+
});
780+
688781
test("commits an on-disk extra turn segment that is not in HEAD without writeTurns", async () => {
689782
const dir = tempDir();
690783
const store = await createOptimizedContextStore(dir);
@@ -1617,6 +1710,33 @@ function readTurnsFromFile(fullPath: string): ConversationTurn[] {
16171710
.map((line) => JSON.parse(line) as ConversationTurn);
16181711
}
16191712

1713+
async function countPathOps(
1714+
op: "readFile" | "lstat",
1715+
marker: string,
1716+
run: () => Promise<unknown>,
1717+
): Promise<number> {
1718+
let hits = 0;
1719+
const target = fs.promises as unknown as Record<
1720+
string,
1721+
(...args: unknown[]) => Promise<unknown>
1722+
>;
1723+
const original = target[op];
1724+
if (original === undefined) {
1725+
throw new Error(`missing fs.promises.${op}`);
1726+
}
1727+
target[op] = (...args: unknown[]) => {
1728+
const first = args[0];
1729+
if (typeof first === "string" && first.includes(marker)) hits += 1;
1730+
return original.apply(fs.promises, args);
1731+
};
1732+
try {
1733+
await run();
1734+
} finally {
1735+
target[op] = original;
1736+
}
1737+
return hits;
1738+
}
1739+
16201740
async function gitLsTree(dir: string): Promise<string[]> {
16211741
const proc = Bun.spawn(
16221742
["git", "-C", dir, "ls-tree", "-r", "--name-only", "HEAD"],

0 commit comments

Comments
 (0)