Skip to content

feat(cache): namespace-versioned cache keys for queries and pipes - #314

Merged
taitelee merged 10 commits into
mainfrom
pipe-cache-invalidation
Jun 10, 2026
Merged

feat(cache): namespace-versioned cache keys for queries and pipes#314
taitelee merged 10 commits into
mainfrom
pipe-cache-invalidation

Conversation

@taitelee

Copy link
Copy Markdown
Member

Summary
Adds a versioned, namespace-based cache-key scheme to internal/cache that underpins cache invalidation for both structured queries and (eventually) named pipes. A cached result is keyed by every (table, scope) it depends on — one for a structured query, several for a pipe — so a write to any dependency invalidates it. Groundwork for per-pipe cache invalidation (#178).

Additive and dormant: it sits alongside the existing flat versions map (which still backs the live Cache.Get/Set path), and nothing in the read/ingest paths is wired to it yet, so no behavior change.

QueryKey folds the table version and namespace version of every (table, scope) a result depends on into its key, so a bump of any dependency misses it. A structured query passes one Namespace; a multi-table query (pipe) passes several — same QueryKey, same BumpTable/BumpNamespace.

Intended invalidation policy (once the ingest worker is wired): a write to a named scope → BumpNamespace(table, scope) (that scope + the whole-table view; other scopes survive); a no-scope / whole-table write → BumpTable(table) (whole table in O(1)).

Need todos:

  • Wire the structured-query handler + ingest worker onto GetQuery / BumpTable / BumpNamespace; retire the legacy flat path.
  • Pipe caching: resolve a pipe's table dependencies and build the []Namespace (lives in the pipes package).
  • Unit tests for the new helpers + a CHANGELOG entry.

From Claude: Heads-up on scope: these changes are groundwork and not active on the live path yet. The new tableVersions/namespaceVersions registries and the BumpTable/BumpNamespace primitives exist, but nothing calls them on a real ingest — the worker still invalidates through the legacy flat versions map, and the query/pipe handlers still read through the legacy Cache.Get/Set. So a real write today does not update the new tables or namespace maps, and GetQuery isn't reachable from any endpoint. Making it effective is a follow-up that wires the handlers onto GetQuery and the worker onto BumpTable/BumpNamespace (choosing which by the ingest's scope), reaching into internal/api and internal/ingest. This PR is intentionally additive with no behavior change.

@taitelee
taitelee requested review from a team and EricAndrechek June 10, 2026 03:01
@github-actions github-actions Bot added go Pull requests that update go code area/cache Local / shared / tiered caching labels Jun 10, 2026
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@taitelee, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 45 minutes and 21 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f4fd0cf0-19c9-47ff-b02d-2ac4bbcf10d5

📥 Commits

Reviewing files that changed from the base of the PR and between 1eeb929 and 3bae7ec.

📒 Files selected for processing (6)
  • CHANGELOG.md
  • internal/api/structured_query.go
  • internal/cache/local.go
  • internal/cache/local_test.go
  • internal/ingest/worker.go
  • internal/ingest/worker_test.go
📝 Walkthrough

Walkthrough

Adds per-table and per-namespace versioning and deterministic query-key construction in VersionManager (Namespace type, NamespaceKey, QueryKey, BumpTable, BumpNamespace). LocalCache.Get/Set now read/write query results using folded keys from (sha, deps) and Invalidate(namespaces) bumps versions. Callers, tests, and mocks updated to the new namespace-shaped API.

Changes

Query-based caching with namespace versioning

Layer / File(s) Summary
Namespace versioning state and types
internal/cache/version_manager.go
Adds Namespace type, imports sort and strings, and extends VersionManager with tableVersions and namespaceVersions maps initialized in NewVersionManager.
Namespace and query key building
internal/cache/version_manager.go
Implements NamespaceKey(table, scope) to render version-qualified namespace keys and QueryKey(sha, deps) to build deterministic, sorted cache-key fragments from dependency namespaces and their versions.
Version advancement and whole-table invalidation
internal/cache/version_manager.go
Implements BumpTable(table) and BumpNamespace(table, scope) to advance version counters under lock; BumpNamespace also increments the whole-table (empty-scope) namespace version when a scoped namespace is bumped.
Cache interface redefinition
internal/cache/cache.go
Reworks Cache interface to accept (ctx, sha string, deps []Namespace) for Get/Set and renames InvalidateCacheInvalidate(ctx, namespaces []Namespace). Removes old generateVersionKey.
Query cache operations
internal/cache/local.go
LocalCache.Get/Set compute folded keys with VersionManager.QueryKey and read/write results with TTL semantics; Invalidate(ctx, namespaces) bumps table and namespace versions per new rules.
Callers: API handlers and pipes
internal/api/structured_query.go, internal/api/pipes.go
StructuredQuery now passes deps []cache.Namespace{ {Table, ""} } to cache calls; pipes use nil deps for TTL-only caching and drop previous scope plumbing.
Ingest worker invalidation
internal/ingest/worker.go
After inserts, ingest deduplicates message scopes and calls cache.Invalidate(ctx, []cache.Namespace{...}) (table+scope) instead of building version-key strings.
Tests and mock updates
internal/cache/*, internal/ingest/*, internal/testutil/mocks.go
Unit tests and MockCache updated to record and assert invalidations as []cache.Namespace; new VersionManager tests validate key ordering and bump behavior.

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Wave-RF/WaveHouse#182 — Related cache namespace/version refactor that overlaps ingest invalidation changes.
  • Wave-RF/WaveHouse#177 — Prior cache internals refactor touching versioned keys and invalidation, overlapping this change.

Suggested labels

area/pipes, area/query, documentation, area/docs

Suggested reviewers

  • EricAndrechek
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.89% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'feat(cache): namespace-versioned cache keys for queries and pipes' directly and specifically describes the main change: introducing namespace-versioned cache keys for two major use cases.
Description check ✅ Passed The PR description comprehensively explains the namespace-versioned cache-key scheme, its intended use cases, the new QueryKey/BumpTable/BumpNamespace primitives, and clearly states the current state is additive/dormant with no behavior change.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pipe-cache-invalidation
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch pipe-cache-invalidation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@taitelee taitelee linked an issue Jun 10, 2026 that may be closed by this pull request
3 tasks
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 10, 2026
@taitelee taitelee moved this from Backlog to In progress in WaveHouse Task Board Jun 10, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 10, 2026
@github-actions github-actions Bot added area/api HTTP handlers, routing, middleware area/ingest Ingest pipeline (Bento, batching, DLQ) labels Jun 10, 2026
Comment thread internal/cache/local.go
@coderabbitai
coderabbitai Bot requested a review from EricAndrechek June 10, 2026 18:25
@coderabbitai coderabbitai Bot added documentation Improvements or additions to documentation area/query Structured query AST, SQL builder area/pipes Named query pipes area/docs Documentation, site/, README labels Jun 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5f93eb70-dbd9-4283-af83-6f8084fcc906

📥 Commits

Reviewing files that changed from the base of the PR and between 218cad0 and 1eeb929.

📒 Files selected for processing (11)
  • internal/api/pipes.go
  • internal/api/structured_query.go
  • internal/cache/cache.go
  • internal/cache/cache_test.go
  • internal/cache/local.go
  • internal/cache/local_test.go
  • internal/cache/version_manager.go
  • internal/cache/version_manager_test.go
  • internal/ingest/worker.go
  • internal/ingest/worker_test.go
  • internal/testutil/mocks.go
💤 Files with no reviewable changes (1)
  • internal/cache/cache_test.go
📜 Review details
⏰ Context from checks skipped due to timeout of 300000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: CI
  • GitHub Check: Analyze (go)
  • GitHub Check: Analyze (go)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Use Go 1.26, strict formatting with gofumpt
Use structured logging with log/slog (JSON handler) in Go code
Use Chi v5 for HTTP routing in Go code
Go error handling: Return errors, don't panic. Wrap with fmt.Errorf("context: %w", err)
No global state in Go code — pass dependencies explicitly via constructor injection
Go package naming: lowercase, single word (or abbreviated), with internal/ enforcing module privacy
Go files must pass gofumpt formatting (enforced by CI)
Go code must pass golangci-lint checks (v2.11.4, auto-installed to .bin/ on first make lint)

Files:

  • internal/api/pipes.go
  • internal/api/structured_query.go
  • internal/testutil/mocks.go
  • internal/ingest/worker.go
  • internal/cache/local.go
  • internal/cache/version_manager_test.go
  • internal/ingest/worker_test.go
  • internal/cache/local_test.go
  • internal/cache/cache.go
  • internal/cache/version_manager.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Use table-driven tests with tests := []struct{ name string; ... } and t.Run(tt.name, ...)
Use shared mocks from internal/testutil/ (MockPublisher, MockCache, MockDeduplicator, MockSubscriber) in Go tests
Use testutil.MakeJWT(t, claims) and testutil.MakeExpiredJWT(t, claims) for auth tests in Go
Use testutil.NewTestSchemaRegistry(tables) or discovery.NewSchemaRegistryFromMap(tables) for schema-aware Go tests
Use policy.NewMemoryStore(p) for in-memory policy testing without NATS in Go tests
Use pipes.NewMemoryStore(queries...) for in-memory pipes testing without NATS in Go tests
Use testutil.AssertJSONResponse(t, rec, status, expected) and testutil.AssertJSONContains(t, rec, status, substring) for response assertions

Files:

  • internal/cache/version_manager_test.go
  • internal/ingest/worker_test.go
  • internal/cache/local_test.go
🧠 Learnings (2)
📚 Learning: 2026-05-20T20:30:15.808Z
Learnt from: taitelee
Repo: Wave-RF/WaveHouse PR: 172
File: internal/api/pipes_test.go:106-118
Timestamp: 2026-05-20T20:30:15.808Z
Learning: For WaveHouse pipes authorization allowlist checks, fix the empty-role fail-open behavior by (1) removing any outer guard that prevents allowlist evaluation when the incoming `role` is `""` (e.g., don’t short-circuit with `if role != "" { ... }`), and (2) during allowlist scanning, ensure only non-empty allowlist entries can match—e.g., require `ar != "" && ar == role` (so a malformed allowlist like `["" ]` cannot grant access to an empty incoming role via `"" == ""`).

Applied to files:

  • internal/api/pipes.go
📚 Learning: 2026-05-25T11:24:21.130Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 180
File: internal/cache/local.go:0-0
Timestamp: 2026-05-25T11:24:21.130Z
Learning: In WaveHouse’s cache packages (e.g., internal/cache/local.go), it’s acceptable to define package-level `var` constants that hold immutable OpenTelemetry metric attribute sets / `metric.MeasurementOption` values (for example: `cacheL1Attrs = metric.WithAttributes(attribute.String("tier","L1"))`). Treat these as stateless, pre-allocated option values (analogous to `regexp.MustCompile(...)`), not mutable global state. When applying the AGENTS.md “no global state / constructor injection” guideline, apply it to application dependencies (e.g., Cache, Publisher, Deduplicator) rather than to these immutable OTel attribute/measurement option variables—do not flag them as constructor-injection violations.

Applied to files:

  • internal/cache/local.go
  • internal/cache/version_manager_test.go
  • internal/cache/local_test.go
  • internal/cache/cache.go
  • internal/cache/version_manager.go
🔇 Additional comments (22)
internal/cache/version_manager.go (5)

1-10: LGTM!


12-32: LGTM!


34-51: LGTM!


53-74: LGTM!


76-94: LGTM!

internal/cache/cache.go (2)

8-32: LGTM!


34-53: LGTM!

internal/cache/local.go (5)

11-29: LGTM!


31-43: LGTM!


45-54: LGTM!


56-87: LGTM!


89-99: LGTM!

internal/api/pipes.go (1)

139-177: LGTM!

internal/api/structured_query.go (1)

134-134: LGTM!

Also applies to: 170-170

internal/ingest/worker.go (2)

481-487: LGTM!


466-479: Confirm empty-scope encoding preserves whole-table cache invalidation
query.SafeEncodeNATS("") returns "" (see internal/query/ident_test.go “empty string” case and internal/query/ident.go implementation), so Scope: query.SafeEncodeNATS(pm.scope) keeps empty scopes empty and matches the internal/cache/cache.go contract for whole-table bumps.

internal/cache/version_manager_test.go (1)

9-72: LGTM!

internal/cache/local_test.go (1)

12-165: LGTM!

internal/ingest/worker_test.go (3)

140-232: LGTM!


449-535: LGTM!


696-756: LGTM!

internal/testutil/mocks.go (1)

117-136: LGTM!

Comment thread internal/api/structured_query.go Outdated
@github-project-automation github-project-automation Bot moved this from In progress to In review in WaveHouse Task Board Jun 10, 2026
@github-actions github-actions Bot removed documentation Improvements or additions to documentation area/query Structured query AST, SQL builder area/pipes Named query pipes labels Jun 10, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/api HTTP handlers, routing, middleware area/cache Local / shared / tiered caching area/docs Documentation, site/, README area/ingest Ingest pipeline (Bento, batching, DLQ) go Pull requests that update go code

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

feat(cache): pipe cache invalidation via table/scope extraction from named queries

2 participants