Call graph: bounded unresolved-site count for empty callers/impact notes (58 s to ~70 ms)

Merged
AF
4d ago
+257-8

When aft_callgraph callers or impact finds no callers, AFT adds a note counting unresolved call sites in that language. To get that count it read every unresolved call/construction ref in the project (unresolved_method_sites_named("")), with a per-row NOT EXISTS and a full GROUP BY + ORDER BY. In tonight's perf sweep one such request held a worker for 59% of a 15 s profile; the worst call took 58 s.

  • The note now uses COUNT(*) over a DISTINCT site subquery with LIMIT 1000 inside the query, with the include-tests filter (enclosing test node, or the shared test-file classifier via a SQLite function) applied in SQL. The named-receiver query is unchanged.
  • Wording is unchanged below the cap. At the cap the note says "at least 1000 call sites ... could not be resolved", and the numeric field holds 1000.
  • Measured on a real store (this repo: 5,447 files, 1,032,186 refs), median of five: Rust 5,600 ms down to 78 ms; TypeScript 3,521 ms down to 69 ms. EXPLAIN QUERY PLAN shows the GROUP BY/ORDER BY temp B-trees gone. A status-leading refs index would help further; it isn't added here (no format bump).
  • Enables rusqlite's existing functions feature; no dependency or lockfile version change.

Tests: exact note text for zero, a few, and over-cap sites, with include-tests on and off; a counted scalar function proves the SQL stops reading at the cap; a regression control that restores the old count fails exactly the cap test. On the build server, 606 callgraph/list-surface library tests, 183 list-envelope tests and the Windows target check passed. One unrelated health contention test failed under load; it passes alone.


Summary by cubic

Speeds up the empty-callers/impact incompleteness note by counting unresolved call sites with a capped SQL COUNT instead of reading every unresolved ref. On a real store, Rust notes dropped from ~5,600 ms to ~78 ms and TypeScript from ~3,521 ms to ~69 ms.

At the cap the note now says "at least 1000 call sites ... could not be resolved" and reports 1000; below the cap the wording is unchanged. The include-tests filter runs in SQL, and the change enables rusqlite's functions feature without a dependency or schema change.