Summary
Store.RebindGoMethodReceivers("") (internal/graph/store_sqlite/method_receiver_rebind.go:85), the SQLite-native fast path for the resolver's rebind_go_method_receivers attribution sub-pass, hung for 2+ hours reindexing the gortex repository itself, and never completed before I gave up and restarted the daemon.
Evidence (confirmed from logs)
Every other occurrence of this exact sub-pass in the same daemon's log history completed in well under a second:
{"ts":..., "msg":"resolver: attribution sub-pass starting","pass":"rebind_go_method_receivers"}
{"ts":..., "msg":"resolver: attribution sub-passes","rebind_go_method_receivers":0.000966664, ...}
(4 separate occurrences across different sessions, all sub-second, one example above)
This occurrence, on a fresh gortex track --wait reindex of the gortex repo itself:
09:07:02 "resolver: attribution sub-pass starting","pass":"rebind_go_method_receivers"
— never logged a completion line. From 09:08:48 through 11:33:48 (2h26m), the log shows a wal checkpoint deferred mode=PASSIVE reason=writer_gate message every 5 minutes without interruption, consistent with something continuously holding the store's write role. I restarted the daemon at 11:35:02 without it ever finishing.
Repo being indexed at the time: gortex itself — 1,997 files, 162,498 nodes, 726,118 edges, 6,251 methods (per gortex status / the repo's own generated CLAUDE.md stats). Every prior fast completion was presumably on a smaller/less method-dense repo — I don't have a log line proving which repo those ran against, so I can't rule out repo-size as the sole variable, but the ~10,000x+ slowdown on this specific run stood out enough to investigate.
Hypothesis (not confirmed — I did not attach a debugger or get an EXPLAIN QUERY PLAN before restarting)
RebindGoMethodReceivers holds s.writeMu for its entire duration (method_receiver_rebind.go:86-87), across:
- The candidate-collection query — a 3-way JOIN (
edges × nodes × nodes, using nodes_go_receiver_type and edges_by_kind indexes) with GROUP BY e.id HAVING COUNT(*) = 1 AND MIN(c.id) <> e.to_id (goMethodReceiverCandidatesGlobalSQL, lines 20-40).
- A 3-statement mutating transaction (conflict-delete, dedup-delete, update).
On a package/dir layout with many same-named Go types clustering under nodes_go_receiver_type (repo_prefix, file_dir, name) — plausible in a codebase this large with common receiver/type names repeated across many packages — the pre-GROUP BY intermediate row set from the join could blow up combinatorially before collapsing, turning what's normally a cheap indexed lookup into a very expensive scan. I have not verified this against EXPLAIN QUERY PLAN — it's the most plausible mechanism I could identify from reading the query, not a confirmed root cause.
Whatever the actual query-plan behavior, holding the single global writeMu for the full duration means every other write-dependent operation (and, per the daemon's own log, hits/subsequent analysis passes queued behind the same store) stalls right along with it — worth decoupling the long-running candidate SELECT from the writer lock if it can safely run under a read/shared-read scope before the short mutating transaction.
Environment
- gortex v0.63.8, linux/amd64
- Repo under index: gortex's own source tree (self-hosted/dogfooding case)
- Store: SQLite backend, default config
What I did
Restarted the daemon rather than continue waiting or attempt to intervene in the live process (didn't want to risk corrupting a stuck write transaction). No repro script yet — this was observed once, on this specific reindex. Happy to help gather EXPLAIN QUERY PLAN output or attach pprof if it recurs and someone wants a live capture before restart.
Summary
Store.RebindGoMethodReceivers("")(internal/graph/store_sqlite/method_receiver_rebind.go:85), the SQLite-native fast path for the resolver'srebind_go_method_receiversattribution sub-pass, hung for 2+ hours reindexing thegortexrepository itself, and never completed before I gave up and restarted the daemon.Evidence (confirmed from logs)
Every other occurrence of this exact sub-pass in the same daemon's log history completed in well under a second:
(4 separate occurrences across different sessions, all sub-second, one example above)
This occurrence, on a fresh
gortex track --waitreindex of thegortexrepo itself:— never logged a completion line. From 09:08:48 through 11:33:48 (2h26m), the log shows a
wal checkpoint deferred mode=PASSIVE reason=writer_gatemessage every 5 minutes without interruption, consistent with something continuously holding the store's write role. I restarted the daemon at 11:35:02 without it ever finishing.Repo being indexed at the time:
gortexitself — 1,997 files, 162,498 nodes, 726,118 edges, 6,251 methods (pergortex status/ the repo's own generated CLAUDE.md stats). Every prior fast completion was presumably on a smaller/less method-dense repo — I don't have a log line proving which repo those ran against, so I can't rule out repo-size as the sole variable, but the ~10,000x+ slowdown on this specific run stood out enough to investigate.Hypothesis (not confirmed — I did not attach a debugger or get an EXPLAIN QUERY PLAN before restarting)
RebindGoMethodReceiversholdss.writeMufor its entire duration (method_receiver_rebind.go:86-87), across:edges×nodes×nodes, usingnodes_go_receiver_typeandedges_by_kindindexes) withGROUP BY e.id HAVING COUNT(*) = 1 AND MIN(c.id) <> e.to_id(goMethodReceiverCandidatesGlobalSQL, lines 20-40).On a package/dir layout with many same-named Go types clustering under
nodes_go_receiver_type(repo_prefix, file_dir, name) — plausible in a codebase this large with common receiver/type names repeated across many packages — the pre-GROUP BYintermediate row set from the join could blow up combinatorially before collapsing, turning what's normally a cheap indexed lookup into a very expensive scan. I have not verified this againstEXPLAIN QUERY PLAN— it's the most plausible mechanism I could identify from reading the query, not a confirmed root cause.Whatever the actual query-plan behavior, holding the single global
writeMufor the full duration means every other write-dependent operation (and, per the daemon's own log,hits/subsequent analysis passes queued behind the same store) stalls right along with it — worth decoupling the long-running candidate SELECT from the writer lock if it can safely run under a read/shared-read scope before the short mutating transaction.Environment
What I did
Restarted the daemon rather than continue waiting or attempt to intervene in the live process (didn't want to risk corrupting a stuck write transaction). No repro script yet — this was observed once, on this specific reindex. Happy to help gather
EXPLAIN QUERY PLANoutput or attachpprofif it recurs and someone wants a live capture before restart.