Skip to content

Commit 4184e0d

Browse files
Merge pull request #1511 from corbitsdev/cl-10815-cut-per-turn-git-re-hashing-and-the-full-statusmatrix-walk
perf(session): stop re-hashing unchanged segments on each commit
2 parents b9efd82 + e06021a commit 4184e0d

2 files changed

Lines changed: 488 additions & 36 deletions

File tree

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

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,155 @@ 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("an external append to a tracked segment is in the next commit", async () => {
713+
const dir = tempDir();
714+
const store = await createOptimizedContextStore(dir);
715+
const chunk = "s".repeat(100_000);
716+
const turns: ConversationTurn[] = [];
717+
const closed = segmentFileName(TURNS_FILE, 1);
718+
const tail = segmentFileName(TURNS_FILE, 2);
719+
do {
720+
turns.push(turn(`${turns.length}-${chunk}`));
721+
await store.writeTurns(turns);
722+
} while (!fs.existsSync(path.join(dir, tail)) && turns.length < 20);
723+
expect(fs.existsSync(path.join(dir, closed))).toBe(true);
724+
await store.writeMetadata(EMPTY_CHECKPOINT_METADATA);
725+
const first = await store.commit({ message: "three segments" });
726+
727+
const marker = "external-drift-bytes";
728+
fs.appendFileSync(path.join(dir, closed), `${marker}\n`);
729+
let secondHash = "";
730+
const unchangedReads = await countPathOps("readFile", tail, async () => {
731+
const second = await store.commit({ message: "publish drifted segment" });
732+
secondHash = second.hash;
733+
});
734+
expect(unchangedReads).toBe(0);
735+
expect(secondHash).not.toBe(first.hash);
736+
const { blob } = await git.readBlob({
737+
fs,
738+
dir,
739+
oid: secondHash,
740+
filepath: closed,
741+
});
742+
expect(new TextDecoder().decode(blob)).toContain(marker);
743+
});
744+
745+
test("a turn commit does not lstat every seeded tool-output blob", async () => {
746+
const dir = tempDir();
747+
const store = await createOptimizedContextStore(dir);
748+
const blobCount = 24;
749+
for (let index = 0; index < blobCount; index++) {
750+
await store.writeBlob(
751+
`blob-${index}`,
752+
new TextEncoder().encode(`body-${index}`),
753+
"text/plain",
754+
);
755+
}
756+
const turns = [turn("seed")];
757+
await store.writeTurns(turns);
758+
await store.writeMetadata(EMPTY_CHECKPOINT_METADATA);
759+
await store.commit({ message: "seed blobs" });
760+
761+
const lstats = await countPathOps(
762+
"lstat",
763+
`${path.sep}tool-output${path.sep}`,
764+
async () => {
765+
turns.push(turn("next"));
766+
await store.writeTurns(turns);
767+
await store.commit({ message: "next turn" });
768+
},
769+
);
770+
// Vendor commit still stats each blob once while staging it. The
771+
// empty-commit matrix must not stat them again on a turn that already
772+
// changed a managed file.
773+
expect(lstats).toBeLessThan(blobCount + blobCount / 2);
774+
});
775+
776+
test("a no-op commit does not lstat every tool-output blob", async () => {
777+
const dir = tempDir();
778+
const store = await createOptimizedContextStore(dir);
779+
const blobCount = 24;
780+
for (let index = 0; index < blobCount; index++) {
781+
await store.writeBlob(
782+
`blob-${index}`,
783+
new TextEncoder().encode(`body-${index}`),
784+
"text/plain",
785+
);
786+
}
787+
await store.writeTurns([turn("seed")]);
788+
await store.writeMetadata(EMPTY_CHECKPOINT_METADATA);
789+
await store.commit({ message: "seed blobs" });
790+
791+
const lstats = await countPathOps(
792+
"lstat",
793+
`${path.sep}tool-output${path.sep}`,
794+
() => store.commit({ message: "no writes" }),
795+
);
796+
expect(lstats).toBe(0);
797+
});
798+
799+
test("seeded tool-output does not commit a no-op and a fresh store still heals blob bytes", async () => {
800+
const dir = tempDir();
801+
const store = await createOptimizedContextStore(dir);
802+
await store.writeBlob(
803+
"kept",
804+
new TextEncoder().encode("original"),
805+
"text/plain",
806+
);
807+
await store.writeTurns([turn("seed")]);
808+
await store.writeMetadata(EMPTY_CHECKPOINT_METADATA);
809+
const first = await store.commit({ message: "seed blob" });
810+
811+
fs.writeFileSync(path.join(dir, "partial.jsonl"), '{"reason":"abort"}\n');
812+
const second = await store.commit({ message: "empty with blobs" });
813+
expect(second.hash).toBe(first.hash);
814+
815+
const blobName = fs
816+
.readdirSync(path.join(dir, "tool-output"))
817+
.find((name) => name.startsWith("kept"));
818+
if (blobName === undefined) throw new Error("seeded blob missing");
819+
fs.writeFileSync(path.join(dir, "tool-output", blobName), "rewritten");
820+
fs.writeFileSync(path.join(dir, "tool-output", "stray.txt"), "stray");
821+
822+
const crashed = await createOptimizedContextStore(dir);
823+
const healed = await crashed.commit({ message: "heal blobs" });
824+
expect(healed.hash).not.toBe(first.hash);
825+
const tree = await gitLsTree(dir);
826+
expect(tree).toContain(`tool-output/${blobName}`);
827+
expect(tree).toContain("tool-output/stray.txt");
828+
const { blob } = await git.readBlob({
829+
fs,
830+
dir,
831+
oid: healed.hash,
832+
filepath: `tool-output/${blobName}`,
833+
});
834+
expect(new TextDecoder().decode(blob)).toBe("rewritten");
835+
});
836+
688837
test("commits an on-disk extra turn segment that is not in HEAD without writeTurns", async () => {
689838
const dir = tempDir();
690839
const store = await createOptimizedContextStore(dir);
@@ -1617,6 +1766,33 @@ function readTurnsFromFile(fullPath: string): ConversationTurn[] {
16171766
.map((line) => JSON.parse(line) as ConversationTurn);
16181767
}
16191768

1769+
async function countPathOps(
1770+
op: "readFile" | "lstat",
1771+
marker: string,
1772+
run: () => Promise<unknown>,
1773+
): Promise<number> {
1774+
let hits = 0;
1775+
const target = fs.promises as unknown as Record<
1776+
string,
1777+
(...args: unknown[]) => Promise<unknown>
1778+
>;
1779+
const original = target[op];
1780+
if (original === undefined) {
1781+
throw new Error(`missing fs.promises.${op}`);
1782+
}
1783+
target[op] = (...args: unknown[]) => {
1784+
const first = args[0];
1785+
if (typeof first === "string" && first.includes(marker)) hits += 1;
1786+
return original.apply(fs.promises, args);
1787+
};
1788+
try {
1789+
await run();
1790+
} finally {
1791+
target[op] = original;
1792+
}
1793+
return hits;
1794+
}
1795+
16201796
async function gitLsTree(dir: string): Promise<string[]> {
16211797
const proc = Bun.spawn(
16221798
["git", "-C", dir, "ls-tree", "-r", "--name-only", "HEAD"],

0 commit comments

Comments
 (0)