Skip to content

perf(graphrag,mcp,storage,ui): scale topology to 100-200 services + labelled, searchable map#124

Merged
aksOps merged 6 commits into
mainfrom
perf/topology-scale-and-map-labels
Jun 19, 2026
Merged

perf(graphrag,mcp,storage,ui): scale topology to 100-200 services + labelled, searchable map#124
aksOps merged 6 commits into
mainfrom
perf/topology-scale-and-map-labels

Conversation

@aksOps

@aksOps aksOps commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Hardens both the SQLite (single-node) and PostgreSQL (scaled) paths for a rolling 7-day window over 100–200 services, and makes the service map usable at that scale.

Backend

  • graphrag: add edgesByFrom/edgesByTo/opsByService adjacency indexes to ServiceStore, maintained at UpsertCallEdge/UpsertOperation/EnsureCallEdge. CallEdgesFrom/To and the new OperationsForService become O(deg) lookups, so ServiceMap drops from O(N·E+N·ops) → O(N+E+ops) and ImpactAnalysis BFS from O(N²) → O(N+E). ServiceMap at 200 svc / 800 edges benchmarks ~53µs.
  • storage: replace the near-useless bare span.status index with composite (tenant_id, status, start_time); unify the trace/orphan-span purge onto the batched LIMIT loop for all drivers so SQLite no longer holds the single writer lock for the whole retention sweep (which stalled ingest).
  • storage: add Postgres BRIN indexes on spans(start_time)/traces(timestamp) for the by-age retention DELETE; raise pool MaxIdleConns 10→25 and add ConnMaxIdleTime (DB_CONN_MAX_IDLE_TIME, default 10m).
  • config: default IngestPipelinePerTenantCap to ~30% of the resolved queue when unset (explicit =0 still disables) so one noisy tenant can't fill the queue.

MCP

  • emit compact JSON (Marshal vs MarshalIndent) across all tool responses.
  • honour the documented get_service_map service focus arg via depth-bounded ServiceMapAround; no focus → full map (backward compatible).
  • cache trace_graph (immutable completed-trace topology; errors not cached).

UI

  • service-map dots now render a haloed, truncated, a11y label whose visibility is zoom-scaled by phyllotaxis criticality band (most-critical label first; emphasized/search-matched always label).
  • add a deferred client-side service search that highlights matches + neighbours and dims the rest, reusing the existing emphasis machinery, with match count, empty state, and clear.

Testing

  • graphrag/mcp/storage/config/ingest Go suites green (incl. new adjacency_index_test.go parity test + BenchmarkServiceMap).
  • UI builds; 339 UI tests pass; changed files lint-clean.

Notes

  • Recommended against range-partitioning spans/traces by default (the unique (tenant_id, trace_id, span_id) constraint makes it unsafe — same reason partitioning was restricted to logs).
  • Deferred (flagged): rebuildRowLimit config knob, NOT EXISTS + traces(trace_id) index swap, threading the MCP per-call ctx into GORM.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EGHhZg96LhYRPQoG8x4nyP

aksOps and others added 2 commits June 18, 2026 13:28
The live OpenSSF record (https://www.bestpractices.dev/projects/12646.json)
is now passing (badge_level=passing, 100%). Reconcile the repo golden source
to match the authoritative live schema:

- Drop 29 vestigial per-criterion <crit>_url keys. The live record has NO
  per-criterion URL field (only the top-level homepage_url / repo_url /
  badge_url / project_page_url), confirming the URL must live inline in the
  justification -- which it now does for all url-required criteria (PR #122).
  The 4 real top-level URL metadata fields are kept.
- Correct _comment to document the inline-URL reality (no separate per-criterion
  URL field) and drop the false "<key>_url where required" clause.
- Simplify report_archive link to /issues to match the live passing value.
- Refresh self_assessment_date to 2026-06-18.

Kept our richer project-specific justifications rather than overwriting them
with bestpractices.dev's auto-generated boilerplate (and a stray OSPS tag);
both validate and the badge stays passing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LDQVJrixs2nJoea67a8pEG
…abelled, searchable map

Hardens both the SQLite (single-node) and Postgres (scaled) paths for a rolling
7-day window over 100-200 services, and makes the service map usable at that scale.

Backend
- graphrag: add edgesByFrom/edgesByTo/opsByService adjacency indexes to
  ServiceStore, maintained at UpsertCallEdge/UpsertOperation/EnsureCallEdge.
  CallEdgesFrom/To and the new OperationsForService become O(deg) lookups, so
  ServiceMap drops from O(N*E+N*ops) to O(N+E+ops) and ImpactAnalysis BFS from
  O(N^2) to O(N+E). ServiceMap at 200 svc / 800 edges benchmarks ~53us.
- storage: replace the near-useless bare span.status index with composite
  (tenant_id, status, start_time); unify the trace/orphan-span purge onto the
  batched LIMIT loop for ALL drivers so SQLite no longer holds the single writer
  lock for the whole retention sweep and stalls ingest.
- storage: add Postgres BRIN indexes on spans(start_time)/traces(timestamp) for
  the by-age retention DELETE; raise pool MaxIdleConns 10->25 and add
  ConnMaxIdleTime (DB_CONN_MAX_IDLE_TIME, default 10m).
- config: default IngestPipelinePerTenantCap to ~30% of the resolved queue when
  unset (explicit =0 still disables) so one noisy tenant cannot fill the queue.

MCP
- emit compact JSON (Marshal vs MarshalIndent) across all tool responses.
- honour the documented get_service_map "service" focus arg via depth-bounded
  ServiceMapAround; no focus -> full map (backward compatible).
- cache trace_graph (immutable completed-trace topology; errors not cached).

UI
- service map dots now render a haloed, truncated, a11y label whose visibility is
  zoom-scaled by phyllotaxis criticality band (most-critical label first;
  emphasized/search-matched always label).
- add a deferred client-side service search that highlights matches + neighbours
  and dims the rest, reusing the existing emphasis machinery, with match count,
  empty state, and clear.

Tests: graphrag/mcp/storage/config/ingest suites green; UI builds + 339 UI tests
pass; changed files lint-clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EGHhZg96LhYRPQoG8x4nyP
Comment thread internal/storage/factory.go Fixed
aksOps and others added 4 commits June 19, 2026 00:03
undici 7.25.0 — a transitive dev dependency via the Vite/test toolchain in
ui/package-lock.json — is flagged by two newly-published advisories:
GHSA-vmh5-mc38-953g (High, 7.4) and GHSA-pr7r-676h-xcf6 (Medium, 5.9), both
fixed in 7.28.0. In-range bump (the parent already allows ^7.25.0), lockfile
only. npm audit reports 0 vulnerabilities; tsc + vitest (339 tests) green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LDQVJrixs2nJoea67a8pEG
The composite-index comment added mid-struct in this PR broke gofmt tag
alignment on the Span block (golangci-lint gofmt failure). Pure
formatting; no behaviour change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LDQVJrixs2nJoea67a8pEG
rfEdges computed the active path only from selection/impact, so a search
accented matched NODES while their connections stayed at resting style —
the 'searchable map' was half-done at 100-200 services where edges are the
signal. Fold searchMatches into the edge active test (precedence: impact >
search > selection, mirroring the node emphasis ladder) and into the memo
deps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LDQVJrixs2nJoea67a8pEG
…ection)

CodeQL flagged factory.go:170 logging the operator-set DB_DRIVER string
(rule go/log-injection, medium) — the merge-blocking unresolved code-scanning
thread. Strip CR/LF via a local var before logging so a malformed value can't
forge log lines; the counts/duration are non-injectable. Operator config (not
a remote-input path), but fix-over-suppress per the security policy, and the
local-var sanitizer is a barrier CodeQL recognises, clearing the alert at the
source. Drops the now-redundant #nosec G706.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LDQVJrixs2nJoea67a8pEG
@aksOps aksOps merged commit d738863 into main Jun 19, 2026
17 checks passed
@aksOps aksOps deleted the perf/topology-scale-and-map-labels branch June 19, 2026 00:27
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants