Skip to content

feat(graphrag): tenant_id column + scoped reads + backfill (RAN-38)#30

Merged
aksOps merged 1 commit into
mainfrom
ran38-graphrag-tenant-persistence-v2
Apr 25, 2026
Merged

feat(graphrag): tenant_id column + scoped reads + backfill (RAN-38)#30
aksOps merged 1 commit into
mainfrom
ran38-graphrag-tenant-persistence-v2

Conversation

@aksOps

@aksOps aksOps commented Apr 25, 2026

Copy link
Copy Markdown
Contributor

Re-opens #28 (auto-closed when its base ran37-graphrag-tenant-partitioned-stores was deleted on RAN-37 merge). Same single commit, rebased onto main post-RAN-37.

Summary

Subtask B of RAN-19. Adds tenant_id to the three persisted GraphRAG tables — investigations, graph_snapshots, drain_templates — with composite indexes, scoped reads via ctx propagation, and a one-shot idempotent startup backfill so existing rows migrate to storage.DefaultTenantID.

  • Schema: investigations and graph_snapshots get (tenant_id, created_at) composite indexes. drain_templates PK is promoted to composite (tenant_id, id) so the same Drain template hash can coexist across tenants once a per-tenant miner ships.
  • Reads: GetInvestigations, GetInvestigation, and GetGraphSnapshot now take a context.Context and filter by storage.TenantFromContext. Cross-tenant id-guess lookups return ErrRecordNotFound.
  • Writes: PersistInvestigation writes its tenant onto the row; takeSnapshotForTenant does the same; SaveDrainTemplates / LoadDrainTemplates now take a tenant parameter and target the composite PK in OnConflict.
  • Cooldown: investigationCooldown keys now include tenant.
  • Migration: AutoMigrateGraphRAG runs three passes — AutoMigrate → idempotent UPDATE … WHERE tenant_id IS NULL OR '' backfill across all three tables → driver-specific drain_templates PK promotion. SQLite uses the rename/recreate/copy/drop recipe; PostgreSQL uses ALTER TABLE … DROP CONSTRAINT IF EXISTS … ADD PRIMARY KEY (tenant_id, id). MySQL/MSSQL skip with a logged warn.

Out of scope

Test plan

  • go vet ./... — clean
  • go test ./internal/graphrag/... ./internal/mcp/... ./internal/storage/... -count=1 — 200 tests pass
  • TestAutoMigrateGraphRAG_CreatesTenantCompositeIndexes — composite indexes materialised on SQLite
  • TestAutoMigrateGraphRAG_DrainTemplatesCompositePK — PRAGMA confirms (tenant_id, id) PK
  • TestAutoMigrateGraphRAG_IsIdempotent — 3× re-run safe
  • TestAutoMigrateGraphRAG_BackfillsLegacyRows — pre-seeded empty-tenant rows are filled with DefaultTenantID
  • TestSaveLoadDrainTemplates_TenantIsolation — colliding template IDs across tenants coexist; cross-tenant Load returns nothing
  • TestGraphRAG_GetInvestigations_TenantScoped — id-guessing across tenants misses
  • TestGraphRAG_GetGraphSnapshot_TenantScoped — same-instant snapshots stay isolated
  • TestCooldownKey_TenantIsolated

🤖 Generated with Claude Code

Adds tenant_id to the three persisted GraphRAG tables (investigations,
graph_snapshots, drain_templates), with a one-shot idempotent backfill so
existing rows fall back to DefaultTenantID and a driver-specific composite
PK promotion for drain_templates on SQLite/PostgreSQL.

- schema.go: TenantID first on DrainTemplateRow; PK becomes (tenant_id, id).
- investigation.go: TenantID + idx_investigations_tenant_created composite
  index; cooldown key now scoped by tenant; GetInvestigations / Get-by-id
  take a context and filter by storage.TenantFromContext.
- snapshot.go: TenantID + idx_graph_snapshots_tenant_created;
  GetGraphSnapshot is ctx-scoped. takeSnapshot continues to fan out per
  tenant.
- drain.go: SaveDrainTemplates / LoadDrainTemplates now take a tenant
  string; OnConflict targets composite (tenant_id, id) so the same
  template hash can coexist across tenants for the eventual per-tenant
  Drain miner.
- migrate.go (new): AutoMigrateGraphRAG runs AutoMigrate, idempotent
  UPDATE backfill, and a SQLite (rename/recreate/copy) + PostgreSQL
  (DROP/ADD CONSTRAINT) PK promotion path. MySQL/MSSQL skipped with a log.
- mcp/tools.go: thread the dispatcher ctx into get_investigations,
  get_investigation, get_graph_snapshot so the new ctx-aware signatures
  compile end-to-end. Subtask C (RAN-39) wires the request tenant.
- builder.go / refresh.go: Drain Save/Load callers pin
  storage.DefaultTenantID for the single shared miner.

Tests:
- TestAutoMigrateGraphRAG_CreatesTenantCompositeIndexes
- TestAutoMigrateGraphRAG_DrainTemplatesCompositePK
- TestAutoMigrateGraphRAG_IsIdempotent
- TestAutoMigrateGraphRAG_BackfillsLegacyRows
- TestSaveLoadDrainTemplates_TenantIsolation (collision across tenants)
- TestGraphRAG_GetInvestigations_TenantScoped (cross-tenant id-guess miss)
- TestGraphRAG_GetGraphSnapshot_TenantScoped
- TestCooldownKey_TenantIsolated

Verification: go vet ./... and go test ./... — 281 tests pass across 26
packages.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Paperclip <noreply@paperclip.ing>
@sonarqubecloud

Copy link
Copy Markdown

@aksOps aksOps merged commit 093d693 into main Apr 25, 2026
11 checks passed
@aksOps aksOps deleted the ran38-graphrag-tenant-persistence-v2 branch April 25, 2026 16:25
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.

1 participant