Symptom (reported by a tester on a ~6M-resource sqlite import)
After a bulk import completes, the hfs process sits at ~100% CPU and the UI looks broken: on Resources every type count shows 0, and the Home dashboard shows inaccurate stats with "Still gathering the live figures…" on the 1h window. Metrics look unavailable. Nothing tells the user why.
Root cause: the post-import deferred search-index rebuild
Bulk import uses fast-load — it stores resources without indexing, then rebuilds the search index in an async background job:
INFO helios_persistence::core::bulk_submit_worker: bulk fast-load: rebuilding deferred search indexes types=[...]
INFO helios_persistence::search::reindex: deferred-index rebuild started job_id=<id>
The job is CPU-bound (it pegs ~100% of one core the whole time) and builds ~20 index rows per resource, so it runs for a long time at scale (measured: 512k resources → 10.3M rows, ~35 min; 6M → ~100M+ rows → hours). Progress is observable at GET /$reindex-status/<jobId> (status / percentage / processed / total).
The data is not lost. Resources are stored; GET /[type]?_summary=count&_total=accurate returns correct numbers straight from the resources table during the rebuild (verified: 11,705 / 299,941 held throughout a reindex). Searches on already-reindexed types work; not-yet-reindexed types return incomplete/0 until the rebuild reaches them (processed in type order). Everything self-heals when $reindex-status → completed and the CPU frees.
Why the UI shows 0 / "gathering"
The Resources type-rail counts and the Home dashboard both read one async DashboardSnapshot (crates/rest/src/dashboard.rs): a TTL-cached count_by_types (GROUP BY on resources, #959) plus a per-type time-series query (count_deltas_by_bucket). Under the reindex's CPU/DB load at 6M scale those snapshot queries are slow / time out; on failure the snapshot is filled with zeros (the partial SnapshotState) or reports "still computing" (the "gathering the live figures" state). At 512k the snapshot computes fast enough that counts stay correct even mid-rebuild — so the breakage is scale-dependent and won't show on a small test DB.
Reproduction
- Import enough resources that the reindex runs for a while (the effect scales with corpus size; most visible in the millions).
- While
GET /$reindex-status/<jobId> reads inprogress, open /ui/resources and /ui — counts read 0 and the dashboard shows "gathering". (On a small DB, trigger it directly with POST /$reindex and watch one core hit ~100%.)
- Confirm the data is intact the whole time via
GET /[type]?_summary=count&_total=accurate.
Suggested fixes
- Surface the rebuild in the UI. A "search index rebuilding — X%" banner on the dashboard and Resources pages (fed by
$reindex-status) so 0 counts + "gathering" read as "indexing, come back later" instead of a broken server.
- Decouple the rail counts from the snapshot. The type-rail counts come from
count_by_types (resources table) and are cheap and correct even during a rebuild; sourcing them directly (not via the failure-prone dashboard snapshot) keeps them accurate while the heavier time-series snapshot is still computing.
Environment: sqlite, all-features release at commit 2b1b0347a, Windows 11. Related: #959 (type-count TTL), #993 (rail fabricated 0).
Symptom (reported by a tester on a ~6M-resource sqlite import)
After a bulk import completes, the
hfsprocess sits at ~100% CPU and the UI looks broken: on Resources every type count shows 0, and the Home dashboard shows inaccurate stats with "Still gathering the live figures…" on the 1h window. Metrics look unavailable. Nothing tells the user why.Root cause: the post-import deferred search-index rebuild
Bulk import uses fast-load — it stores resources without indexing, then rebuilds the search index in an async background job:
The job is CPU-bound (it pegs ~100% of one core the whole time) and builds ~20 index rows per resource, so it runs for a long time at scale (measured: 512k resources → 10.3M rows, ~35 min; 6M → ~100M+ rows → hours). Progress is observable at
GET /$reindex-status/<jobId>(status/percentage/processed/total).The data is not lost. Resources are stored;
GET /[type]?_summary=count&_total=accuratereturns correct numbers straight from theresourcestable during the rebuild (verified: 11,705 / 299,941 held throughout a reindex). Searches on already-reindexed types work; not-yet-reindexed types return incomplete/0 until the rebuild reaches them (processed in type order). Everything self-heals when$reindex-status→completedand the CPU frees.Why the UI shows 0 / "gathering"
The Resources type-rail counts and the Home dashboard both read one async
DashboardSnapshot(crates/rest/src/dashboard.rs): a TTL-cachedcount_by_types(GROUP BY onresources, #959) plus a per-type time-series query (count_deltas_by_bucket). Under the reindex's CPU/DB load at 6M scale those snapshot queries are slow / time out; on failure the snapshot is filled with zeros (thepartialSnapshotState) or reports "still computing" (the "gathering the live figures" state). At 512k the snapshot computes fast enough that counts stay correct even mid-rebuild — so the breakage is scale-dependent and won't show on a small test DB.Reproduction
GET /$reindex-status/<jobId>readsinprogress, open/ui/resourcesand/ui— counts read 0 and the dashboard shows "gathering". (On a small DB, trigger it directly withPOST /$reindexand watch one core hit ~100%.)GET /[type]?_summary=count&_total=accurate.Suggested fixes
$reindex-status) so 0 counts + "gathering" read as "indexing, come back later" instead of a broken server.count_by_types(resources table) and are cheap and correct even during a rebuild; sourcing them directly (not via the failure-prone dashboard snapshot) keeps them accurate while the heavier time-series snapshot is still computing.Environment: sqlite, all-features release at commit
2b1b0347a, Windows 11. Related: #959 (type-count TTL), #993 (rail fabricated 0).