mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-07 02:58:02 +00:00
fix(cross-file): remove wall-clock timeout from type propagation
The query compilation cache and live progress reporting address the original stall; the 2-minute deadline could truncate cross-file work on large repos. MAX_CROSS_FILE_REPROCESS (2000) remains as the only cap.
This commit is contained in:
parent
ea760adf4c
commit
9ba5d37865
2 changed files with 0 additions and 92 deletions
|
|
@ -37,19 +37,6 @@ const CROSS_FILE_SKIP_THRESHOLD = 0.03;
|
|||
/** Hard cap on files re-processed during cross-file propagation. */
|
||||
const MAX_CROSS_FILE_REPROCESS = 2000;
|
||||
|
||||
/**
|
||||
* Default wall-clock time limit (ms) for cross-file propagation.
|
||||
* Override via `GITNEXUS_CROSS_FILE_TIMEOUT_MS` env var.
|
||||
* Prevents the phase from stalling for hours on very large repositories
|
||||
* where per-file re-resolution is expensive.
|
||||
*/
|
||||
const DEFAULT_CROSS_FILE_ELAPSED_MS = 120_000; // 2 minutes
|
||||
|
||||
function getCrossFileTimeoutMs(): number {
|
||||
const raw = Number(process.env.GITNEXUS_CROSS_FILE_TIMEOUT_MS);
|
||||
return raw > 0 && Number.isFinite(raw) ? raw : DEFAULT_CROSS_FILE_ELAPSED_MS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cross-file binding propagation.
|
||||
* Returns the number of files re-processed.
|
||||
|
|
@ -131,13 +118,11 @@ export async function runCrossFileBindingPropagation(
|
|||
|
||||
let crossFileResolved = 0;
|
||||
const crossFileStart = Date.now();
|
||||
const crossFileDeadlineMs = getCrossFileTimeoutMs();
|
||||
const astCache = createASTCache(AST_CACHE_CAP);
|
||||
// Compiled query objects keyed by language name. Shared across all processCalls
|
||||
// invocations in this phase so the same tree-sitter query string is only
|
||||
// compiled once per language instead of once per file (O(1) vs O(N)).
|
||||
const compiledQueryCache = new Map<SupportedLanguages, Parser.Query>();
|
||||
let timedOut = false;
|
||||
|
||||
// Snapshot total topological candidates for progress math. We walk the
|
||||
// levels once more here (fast — no I/O) so we can report meaningful
|
||||
|
|
@ -161,8 +146,6 @@ export async function runCrossFileBindingPropagation(
|
|||
const PROGRESS_INTERVAL = 25;
|
||||
|
||||
for (const level of levels) {
|
||||
if (timedOut) break;
|
||||
|
||||
const levelCandidates: {
|
||||
filePath: string;
|
||||
seeded: Map<string, string>;
|
||||
|
|
@ -208,15 +191,6 @@ export async function runCrossFileBindingPropagation(
|
|||
const contentMap = await readFileContents(repoPath, levelPaths);
|
||||
|
||||
for (const { filePath, seeded, importedReturns, importedRawReturns } of levelCandidates) {
|
||||
if (Date.now() - crossFileStart >= crossFileDeadlineMs) {
|
||||
timedOut = true;
|
||||
logger.warn(
|
||||
`⏱️ Cross-file type propagation timed out after ${crossFileDeadlineMs}ms` +
|
||||
` (${crossFileResolved} files processed). Override with GITNEXUS_CROSS_FILE_TIMEOUT_MS env var.`,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
const content = contentMap.get(filePath);
|
||||
if (!content) continue;
|
||||
|
||||
|
|
|
|||
|
|
@ -162,72 +162,6 @@ describe('runCrossFileBindingPropagation', () => {
|
|||
}
|
||||
});
|
||||
|
||||
it('stops processing when time deadline is exceeded', async () => {
|
||||
const graph = createKnowledgeGraph();
|
||||
const ctx = createResolutionContext();
|
||||
|
||||
const exportedTypeMap: ExportedTypeMap = new Map([
|
||||
['upstream.ts', new Map([['User', 'User']])],
|
||||
]);
|
||||
|
||||
const allPaths: string[] = ['upstream.ts'];
|
||||
// Create 50 downstream importers
|
||||
ctx.importMap.set('upstream.ts', new Set());
|
||||
for (let i = 0; i < 50; i++) {
|
||||
const file = `downstream${i}.ts`;
|
||||
allPaths.push(file);
|
||||
const bindings = new Map();
|
||||
bindings.set('User', { sourcePath: 'upstream.ts', exportedName: 'User' });
|
||||
ctx.namedImportMap.set(file, bindings);
|
||||
ctx.importMap.set(file, new Set(['upstream.ts']));
|
||||
}
|
||||
|
||||
// Set a very short timeout via env var. The processCalls mock below
|
||||
// advances Date.now past this deadline after the first invocation.
|
||||
const origEnv = process.env.GITNEXUS_CROSS_FILE_TIMEOUT_MS;
|
||||
process.env.GITNEXUS_CROSS_FILE_TIMEOUT_MS = '50';
|
||||
|
||||
let callCount = 0;
|
||||
processCallsMock.mockImplementation(async () => {
|
||||
callCount++;
|
||||
// After the first call, busy-wait past the 50ms deadline so the
|
||||
// time check before the next candidate triggers.
|
||||
if (callCount === 1) {
|
||||
const start = Date.now();
|
||||
while (Date.now() - start < 60) {
|
||||
/* spin */
|
||||
}
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
try {
|
||||
const result = await runCrossFileBindingPropagation(
|
||||
graph,
|
||||
ctx,
|
||||
exportedTypeMap,
|
||||
new Set(allPaths),
|
||||
allPaths.length,
|
||||
'/repo',
|
||||
Date.now(),
|
||||
() => {},
|
||||
);
|
||||
|
||||
// Should have processed at least 1 file (the first one goes through
|
||||
// before the deadline check), but NOT all 50.
|
||||
expect(result).toBeGreaterThan(0);
|
||||
expect(result).toBeLessThan(50);
|
||||
expect(processCallsMock.mock.calls.length).toBeLessThan(50);
|
||||
} finally {
|
||||
if (origEnv === undefined) {
|
||||
delete process.env.GITNEXUS_CROSS_FILE_TIMEOUT_MS;
|
||||
} else {
|
||||
process.env.GITNEXUS_CROSS_FILE_TIMEOUT_MS = origEnv;
|
||||
}
|
||||
processCallsMock.mockImplementation(async () => []);
|
||||
}
|
||||
});
|
||||
|
||||
it('caps processing at MAX_CROSS_FILE_REPROCESS (2000)', async () => {
|
||||
const graph = createKnowledgeGraph();
|
||||
const ctx = createResolutionContext();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue