mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-10 22:43:40 +00:00
* perf(store): add best-effort V8 sidecars beside canonical JSON caches Warm ParsedFile and parse-cache loads skip JSON.parse when a sidecar is present. JSON remains authoritative: envelope validation plus v8.deserialize decide the hit, and any failure falls back without reparsing. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(store): require generation bind or sidecar drop before cache overwrite A same-length JSON rewrite could accept a leftover V8 sidecar if both generation rotation and unlink failed. Refuse the new generation unless at least one of those invalidations succeeds; skip publishing a sidecar when only the drop succeeded. detect_changes --scope all: 7 files, risk low, no affected processes. tsc --noEmit clean; 115/115 relevant unit tests; cache-related integration tests pass. parse-impl-env-reads worker-ready timeout is pre-existing (same 5 failures with this change set stashed). ESLint 0 errors; remaining warnings are pre-existing and not on changed lines. Co-authored-by: Cursor <cursoragent@cursor.com> * chore(autofix): apply prettier + eslint fixes via /autofix command * refactor(store): share V8 overwrite invalidation across persist paths The bind-or-drop gate lived in five writers. One helper keeps the protocol in a single place and lets bind/drop run together on the async path. detect_changes --scope all: 3 files, risk low, no affected processes. Co-authored-by: Cursor <cursoragent@cursor.com> * perf(store): hardlink durable ParsedFile shards into the run store Warm restore of parsedfile-cache into parsedfile-store now publishes all four shard files via fs.link, falling back to copy-into-tmp + rename so a leftover dest hardlink can never be written through. JSON remains the canonical cache; V8 sidecars ride the same path. Co-authored-by: Cursor <cursoragent@cursor.com> * perf(store): load immutable V8 shards in place, drop JSON fallback Warm analyze was still paying JSON.parse plus a restore copy. One .v8 envelope per shard and SCHEMA_BUMP 81 make a miss re-extract instead of serving a stale JSON twin. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(store): validate durable V8 warm-cache restores Reject incomplete or corrupt durable generations and snapshot valid shards before skipping parse workers, preserving ParsedFiles when persistence fails. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(store): drop unused durable load path Load ParsedFiles only from the run-store snapshot and share one checksummed payload reader so inspect and deserialize stay consistent. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
204 lines
7.8 KiB
TypeScript
204 lines
7.8 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { mkdtemp, rm, writeFile, readFile } from 'fs/promises';
|
|
import { tmpdir } from 'os';
|
|
import path from 'path';
|
|
import {
|
|
inspectV8Cache,
|
|
internGraphStrings,
|
|
parseCachePathListing,
|
|
tryLoadV8Cache,
|
|
writeV8CacheFile,
|
|
V8_CACHE_FORMAT,
|
|
} from '../../src/storage/v8-sidecar.js';
|
|
|
|
describe('v8 cache envelope', () => {
|
|
it('interns duplicate strings in place and keeps Map identity', () => {
|
|
const pool = new Map<string, string>();
|
|
const m = new Map([['k', 'dup']]);
|
|
const graph = { a: 'dup', b: 'dup', m };
|
|
internGraphStrings(graph, pool);
|
|
expect(graph.a).toBe(graph.b);
|
|
expect(graph.m).toBe(m);
|
|
expect(graph.m.get('k')).toBe(graph.a);
|
|
expect([...pool.keys()].sort()).toEqual(['dup', 'k']);
|
|
expect(pool.get('dup')).toBe('dup');
|
|
});
|
|
|
|
it('round-trips a live graph including Maps', async () => {
|
|
const dir = await mkdtemp(path.join(tmpdir(), 'v8cf-'));
|
|
try {
|
|
const filePath = path.join(dir, 'shard.v8');
|
|
const graph = { n: 1, nested: { s: 'x' }, map: new Map([['a', 1]]) };
|
|
expect(await writeV8CacheFile(filePath, graph)).toBe(true);
|
|
const hit = await tryLoadV8Cache(filePath);
|
|
expect(hit?.kind).toBe('hit');
|
|
if (hit?.kind !== 'hit') return;
|
|
const value = hit.value as typeof graph;
|
|
expect(value.n).toBe(1);
|
|
expect(value.map).toBeInstanceOf(Map);
|
|
expect(value.map.get('a')).toBe(1);
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('skips deserialize when a valid path listing misses wantPaths', async () => {
|
|
const dir = await mkdtemp(path.join(tmpdir(), 'v8cf-skip-'));
|
|
try {
|
|
const filePath = path.join(dir, 'shard.v8');
|
|
await writeV8CacheFile(filePath, [{ filePath: 'a.c' }], ['a.c']);
|
|
const skip = await tryLoadV8Cache(filePath, undefined, new Set(['other.c']));
|
|
expect(skip?.kind).toBe('skip');
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('misses when the path listing bytes are corrupted', async () => {
|
|
const dir = await mkdtemp(path.join(tmpdir(), 'v8cf-failclosed-'));
|
|
try {
|
|
const filePath = path.join(dir, 'shard.v8');
|
|
await writeV8CacheFile(filePath, [{ filePath: 'a.c' }], ['a.c']);
|
|
const buf = await readFile(filePath);
|
|
const v8len = buf.readUInt16LE(14);
|
|
const pathBytesOff = 16 + v8len + 4;
|
|
const pathBytes = buf.readUInt32LE(pathBytesOff);
|
|
const pathsOff = 16 + v8len + 12;
|
|
buf.fill(0x00, pathsOff, pathsOff + Math.min(pathBytes, 1));
|
|
await writeFile(filePath, buf);
|
|
const hit = await tryLoadV8Cache(filePath, undefined, new Set(['other.c']));
|
|
expect(hit).toBeUndefined();
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('misses when a same-length path listing is rewritten without the digest', async () => {
|
|
const dir = await mkdtemp(path.join(tmpdir(), 'v8cf-list-tamper-'));
|
|
try {
|
|
const filePath = path.join(dir, 'shard.v8');
|
|
await writeV8CacheFile(filePath, [{ filePath: 'a.c' }], ['a.c']);
|
|
const buf = await readFile(filePath);
|
|
const v8len = buf.readUInt16LE(14);
|
|
const pathsOff = 16 + v8len + 12;
|
|
const listing = Buffer.from('1\nb.c\n');
|
|
listing.copy(buf, pathsOff);
|
|
await writeFile(filePath, buf);
|
|
expect(await inspectV8Cache(filePath)).toBeUndefined();
|
|
expect(await tryLoadV8Cache(filePath, undefined, new Set(['other.c']))).toBeUndefined();
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('treats invalid UTF-8 path listing bytes as untrusted', () => {
|
|
expect(parseCachePathListing(Buffer.from([0x31, 0x0a, 0xff, 0x0a]))).toBeNull();
|
|
});
|
|
|
|
it('misses when the path listing is invalid UTF-8 instead of skipping', async () => {
|
|
const dir = await mkdtemp(path.join(tmpdir(), 'v8cf-utf8-'));
|
|
try {
|
|
const filePath = path.join(dir, 'shard.v8');
|
|
await writeV8CacheFile(filePath, [{ filePath: 'a.c' }], ['a.c']);
|
|
const buf = await readFile(filePath);
|
|
const v8len = buf.readUInt16LE(14);
|
|
const pathBytes = buf.readUInt32LE(16 + v8len + 4);
|
|
const pathsOff = 16 + v8len + 12;
|
|
buf.fill(0xff, pathsOff, pathsOff + Math.min(pathBytes, 1));
|
|
await writeFile(filePath, buf);
|
|
const hit = await tryLoadV8Cache(filePath, undefined, new Set(['other.c']));
|
|
expect(hit).toBeUndefined();
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('deserializes when the envelope path count disagrees with the listing', async () => {
|
|
const dir = await mkdtemp(path.join(tmpdir(), 'v8cf-count-'));
|
|
try {
|
|
const filePath = path.join(dir, 'shard.v8');
|
|
await writeV8CacheFile(filePath, [{ filePath: 'a.c' }], ['a.c']);
|
|
const buf = await readFile(filePath);
|
|
const v8len = buf.readUInt16LE(14);
|
|
buf.writeUInt32LE(2, 16 + v8len);
|
|
await writeFile(filePath, buf);
|
|
expect(await inspectV8Cache(filePath)).toBeUndefined();
|
|
const hit = await tryLoadV8Cache(filePath, undefined, new Set(['other.c']));
|
|
expect(hit?.kind).toBe('hit');
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('misses when internGraphStrings throws during materialization', async () => {
|
|
const dir = await mkdtemp(path.join(tmpdir(), 'v8cf-intern-'));
|
|
try {
|
|
const filePath = path.join(dir, 'shard.v8');
|
|
await writeV8CacheFile(filePath, { s: 'x' });
|
|
const pool = new Map<string, string>();
|
|
pool.set = () => {
|
|
throw new Error('intern boom');
|
|
};
|
|
expect(await tryLoadV8Cache(filePath, pool)).toBeUndefined();
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('misses when the recorded Node major does not match this runtime', async () => {
|
|
const dir = await mkdtemp(path.join(tmpdir(), 'v8cf-compat-'));
|
|
try {
|
|
const filePath = path.join(dir, 'shard.v8');
|
|
await writeV8CacheFile(filePath, { ok: true });
|
|
const buf = await readFile(filePath);
|
|
buf.writeUInt16LE(1, 12);
|
|
await writeFile(filePath, buf);
|
|
expect(await tryLoadV8Cache(filePath)).toBeUndefined();
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('misses when the recorded V8 version does not match this runtime', async () => {
|
|
const dir = await mkdtemp(path.join(tmpdir(), 'v8cf-v8-compat-'));
|
|
try {
|
|
const filePath = path.join(dir, 'shard.v8');
|
|
await writeV8CacheFile(filePath, { ok: true });
|
|
const buf = await readFile(filePath);
|
|
buf[16] ^= 1;
|
|
await writeFile(filePath, buf);
|
|
expect(await tryLoadV8Cache(filePath)).toBeUndefined();
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('misses when the payload checksum rejects same-size corruption', async () => {
|
|
const dir = await mkdtemp(path.join(tmpdir(), 'v8cf-payload-'));
|
|
try {
|
|
const filePath = path.join(dir, 'shard.v8');
|
|
await writeV8CacheFile(filePath, { ok: true });
|
|
const buf = await readFile(filePath);
|
|
expect(buf.readUInt32LE(8)).toBe(V8_CACHE_FORMAT);
|
|
const v8len = buf.readUInt16LE(14);
|
|
const pathBytes = buf.readUInt32LE(16 + v8len + 4);
|
|
const payloadOff = 16 + v8len + 12 + pathBytes;
|
|
buf[payloadOff] ^= 0xff;
|
|
await writeFile(filePath, buf);
|
|
expect(await tryLoadV8Cache(filePath)).toBeUndefined();
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('misses a garbage magic without throwing', async () => {
|
|
const dir = await mkdtemp(path.join(tmpdir(), 'v8cf-magic-'));
|
|
try {
|
|
const filePath = path.join(dir, 'shard.v8');
|
|
await writeFile(filePath, Buffer.alloc(64, 7));
|
|
expect(await tryLoadV8Cache(filePath)).toBeUndefined();
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
});
|