add cached tokens to matview; allow matview refersh on the fly - #5507
Conversation
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 15 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (8)
📝 WalkthroughWalkthroughMaterialized-view statistics now include cache-hit aggregates, and PostgreSQL matview read failures fall back to raw tables. Shape failures trigger throttled background repair. Webhook-related enum values gain database serialization, and ranking canonicalization uses live comparison keys. ChangesMatview resilience and statistics
Database serialization and parity
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
framework/logstore/matviewheal.go (1)
79-108: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winAdd panic recovery to the self-heal goroutine.
The goroutine has no
recover(); a panic insideensureMatViews/refreshMatViews(not visible in this diff) would propagate up and crash the entire process. For a best-effort background repair path that is expected to fail gracefully (as the surrounding logging/fallback design already assumes), an unrecovered panic is a disproportionate blast radius.🛡️ Proposed fix
go func() { defer s.matViewHealInFlight.Store(false) + defer func() { + if r := recover(); r != nil && s.logger != nil { + s.logger.Warn(fmt.Sprintf("logstore: matview self-heal panicked: %v", r)) + } + }() if time.Since(time.Unix(0, s.matViewHealLastAttempt.Load())) < matViewHealCooldown { return }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@framework/logstore/matviewheal.go` around lines 79 - 108, Add panic recovery to the background goroutine created by triggerMatViewSelfHeal, ensuring the existing matViewHealInFlight reset still runs and a recovered panic is logged through s.logger when available. Keep the self-heal path best-effort so panics from ensureMatViews or refreshMatViews do not escape and terminate the process.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@framework/logstore/matviewheal.go`:
- Around line 91-102: The matview self-heal flow must retry after
refreshMatViews fails instead of permanently remaining on raw tables. Update the
refresh failure handling around ensureMatViews and refreshMatViews to re-arm or
independently schedule self-heal retries without requiring another shape error,
while preserving the existing warning and raw-table fallback behavior.
---
Nitpick comments:
In `@framework/logstore/matviewheal.go`:
- Around line 79-108: Add panic recovery to the background goroutine created by
triggerMatViewSelfHeal, ensuring the existing matViewHealInFlight reset still
runs and a recovered panic is logged through s.logger when available. Keep the
self-heal path best-effort so panics from ensureMatViews or refreshMatViews do
not escape and terminate the process.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 2a51f375-8f28-47f8-8ea1-e471b5e6149d
📒 Files selected for processing (5)
framework/logstore/matview_count_test.goframework/logstore/matviewheal.goframework/logstore/matviewheal_test.goframework/logstore/matviews.goframework/logstore/rdb.go
0d42413 to
39d00a1
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
framework/logstore/matview_count_test.go (1)
270-346: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDocstring claims raw/matview parity for the nil and zero contracts, but only the main window checks it.
The doc comment says the test verifies the matview path "agrees exactly with the raw path, including the nil contract" and the zero contract. In the actual body,
store.matViewsReadyis only flipped tofalseand compared around the main window (lines 318-325); the nil-contract window (329-334) and zero-contract window (338-345) are only ever exercised through the matview path (matViewsReadystaystrue), so the raw-path equivalence for those two contracts is asserted by the docstring but not actually tested.🧪 Proposed fix to close the coverage gap
filters = SearchFilters{StartTime: &nilStart, EndTime: &nilEnd} require.True(t, store.canUseMatViewForFreshAggregate(filters)) stats, err = store.GetStats(ctx, filters) require.NoError(t, err) assert.Nil(t, stats.DirectCacheHits, "no cache rows -> nil, matching the raw path contract") assert.Nil(t, stats.SemanticCacheHits) + + store.matViewsReady.Store(false) + rawStats, err = store.GetStats(ctx, filters) + require.NoError(t, err) + assert.Nil(t, rawStats.DirectCacheHits, "raw path must also stay nil for the same window") + assert.Nil(t, rawStats.SemanticCacheHits) + store.matViewsReady.Store(true) // Zero contract: cache rows exist but none direct/semantic -> explicit // zeros on both paths. filters = SearchFilters{StartTime: &zeroStart, EndTime: &zeroEnd} require.True(t, store.canUseMatViewForFreshAggregate(filters)) stats, err = store.GetStats(ctx, filters) require.NoError(t, err) require.NotNil(t, stats.DirectCacheHits, "cache rows present -> explicit zeros, not omission") require.NotNil(t, stats.SemanticCacheHits) assert.Equal(t, int64(0), *stats.DirectCacheHits) assert.Equal(t, int64(0), *stats.SemanticCacheHits) + + store.matViewsReady.Store(false) + rawStats, err = store.GetStats(ctx, filters) + require.NoError(t, err) + require.NotNil(t, rawStats.DirectCacheHits) + require.NotNil(t, rawStats.SemanticCacheHits) + assert.Equal(t, int64(0), *rawStats.DirectCacheHits) + assert.Equal(t, int64(0), *rawStats.SemanticCacheHits) + store.matViewsReady.Store(true) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@framework/logstore/matview_count_test.go` around lines 270 - 346, Extend TestGetStatsMatViewCacheHitsHybrid so the nil-contract and zero-contract windows are each executed through both matview and raw paths. Capture and assert the matview results first, set store.matViewsReady to false and repeat GetStats for the same filters, then compare direct and semantic cache-hit pointers/values—including nil versus explicit zero—and restore matViewsReady afterward.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@framework/logstore/matview_count_test.go`:
- Around line 270-346: Extend TestGetStatsMatViewCacheHitsHybrid so the
nil-contract and zero-contract windows are each executed through both matview
and raw paths. Capture and assert the matview results first, set
store.matViewsReady to false and repeat GetStats for the same filters, then
compare direct and semantic cache-hit pointers/values—including nil versus
explicit zero—and restore matViewsReady afterward.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: bf6481fd-11ef-46e9-9d52-301d9acdc3d6
📒 Files selected for processing (8)
framework/configstore/tables/webhooks.goframework/logstore/logstoreparity_test.goframework/logstore/matview_count_test.goframework/logstore/matviewheal.goframework/logstore/matviewheal_test.goframework/logstore/matviews.goframework/logstore/rdb.goframework/logstore/tables.go
🚧 Files skipped from review as they are similar to previous changes (4)
- framework/logstore/matviewheal.go
- framework/logstore/matviews.go
- framework/logstore/matviewheal_test.go
- framework/logstore/rdb.go
39d00a1 to
5b4b7fe
Compare
82d99fb to
f38a29f
Compare
5b4b7fe to
560241a
Compare
f38a29f to
b5890b5
Compare
560241a to
948326e
Compare
The base branch was changed.
948326e to
d704eac
Compare
Merge activity
|
## Summary This PR adds two improvements to the materialized view read path: cache-hit statistics are now served from the hybrid aggregate (interior buckets from `mv_logs_hourly`, boundary slivers classified raw) instead of a separate full-window raw scan, and a runtime self-heal mechanism automatically recovers from missing or stale-shaped materialized views without requiring a process restart. ## Changes - **Cache hits in the hybrid aggregate**: Three new columns (`direct_cache_hits`, `semantic_cache_hits`, `cache_debug_count`) are added to `mv_logs_hourly` using the same `cacheDebugJSONGuard` and `cacheDebugHitTypeExpr` expressions shared across the matview DDL, boundary sliver queries, and `aggregateCacheHits`. `cache_debug_count` preserves the nil contract: when no row in the window carried valid `cache_debug` JSON the fields are omitted from the response, and when cache rows exist but none were direct/semantic explicit zeros are returned. The previous approach issued a separate full-window raw scan for cache hits after the hybrid aggregate completed; that scan is removed. - **Runtime matview self-heal** (`matviewheal.go`): `isMatViewShapeError` classifies PostgreSQL error codes `42P01` (undefined table), `42703` (undefined column), and `55000` (object not in prerequisite state) as shape errors. `fallBackToRaw` is called at every matview dispatch site — on a shape error it disables the matview read path process-wide, logs a warning, and triggers a single-flight background repair via `triggerMatViewSelfHeal`. The repair runs `ensureMatViews` then `refreshMatViews` and re-enables the path on success. A 30-second cooldown (`matViewHealCooldown`) bounds repair frequency; while broken, every request continues succeeding via the raw fallback. Two new atomic fields (`matViewHealInFlight`, `matViewHealLastAttempt`) are added to `RDBLogStore`. - **Shared SQL constants**: The inline regex strings for the cache debug guard and hit-type extractor are replaced with named constants `cacheDebugJSONGuard` and `cacheDebugHitTypeExpr`, used consistently across the matview DDL, `applyFilters`, `rawTerminalStatsAgg`, and `aggregateCacheHits`. - **Tests**: `TestGetStatsMatViewCacheHitsHybrid` verifies the hybrid cache-hit path including nil and zero contracts and agreement with the raw path. `TestMatViewShapeErrorFallsBackAndSelfHeals`, `TestMatViewStaleShapeFallsBackAndSelfHeals`, and `TestFilterMatViewShapeErrorFallsBack` cover the 42P01, 42703, and filter-view drop scenarios end-to-end, including background self-heal convergence. ## Type of change - [x] Bug fix - [x] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [ ] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [ ] UI (React) - [ ] Docs ## How to test ```sh go test ./framework/logstore/... -run TestGetStatsMatViewCacheHitsHybrid go test ./framework/logstore/... -run TestMatViewShapeErrorFallsBackAndSelfHeals go test ./framework/logstore/... -run TestMatViewStaleShapeFallsBackAndSelfHeals go test ./framework/logstore/... -run TestFilterMatViewShapeErrorFallsBack go test ./framework/logstore/... -run TestIsMatViewShapeError go test ./framework/logstore/... ``` The self-heal tests drop or replace `mv_logs_hourly` mid-run and assert that reads continue returning correct results from the raw table with no error, that `matViewsReady` is set to false immediately, and that the view is recreated with the correct shape within 90 seconds. ## Breaking changes - [x] No The new matview columns require a schema migration. On first deploy, `repairMatViewShapes` detects the missing columns, drops and recreates `mv_logs_hourly`, and the self-heal path handles any replica that reads before the rebuild completes. ## Related issues Closes #5384 ## Security considerations No auth, secrets, PII, or sandboxing changes. The new SQL expressions are constants composed only of built-in PostgreSQL operators and are not user-controlled. ## Checklist - [ ] I read `docs/contributing/README.md` and followed the guidelines - [x] I added/updated tests where appropriate - [ ] I updated documentation where needed - [x] I verified builds succeed (Go and UI) - [ ] I verified the CI pipeline passes locally if applicable
This PR adds two improvements to the materialized view read path: cache-hit statistics are now served from the hybrid aggregate (interior buckets from `mv_logs_hourly`, boundary slivers classified raw) instead of a separate full-window raw scan, and a runtime self-heal mechanism automatically recovers from missing or stale-shaped materialized views without requiring a process restart. - **Cache hits in the hybrid aggregate**: Three new columns (`direct_cache_hits`, `semantic_cache_hits`, `cache_debug_count`) are added to `mv_logs_hourly` using the same `cacheDebugJSONGuard` and `cacheDebugHitTypeExpr` expressions shared across the matview DDL, boundary sliver queries, and `aggregateCacheHits`. `cache_debug_count` preserves the nil contract: when no row in the window carried valid `cache_debug` JSON the fields are omitted from the response, and when cache rows exist but none were direct/semantic explicit zeros are returned. The previous approach issued a separate full-window raw scan for cache hits after the hybrid aggregate completed; that scan is removed. - **Runtime matview self-heal** (`matviewheal.go`): `isMatViewShapeError` classifies PostgreSQL error codes `42P01` (undefined table), `42703` (undefined column), and `55000` (object not in prerequisite state) as shape errors. `fallBackToRaw` is called at every matview dispatch site — on a shape error it disables the matview read path process-wide, logs a warning, and triggers a single-flight background repair via `triggerMatViewSelfHeal`. The repair runs `ensureMatViews` then `refreshMatViews` and re-enables the path on success. A 30-second cooldown (`matViewHealCooldown`) bounds repair frequency; while broken, every request continues succeeding via the raw fallback. Two new atomic fields (`matViewHealInFlight`, `matViewHealLastAttempt`) are added to `RDBLogStore`. - **Shared SQL constants**: The inline regex strings for the cache debug guard and hit-type extractor are replaced with named constants `cacheDebugJSONGuard` and `cacheDebugHitTypeExpr`, used consistently across the matview DDL, `applyFilters`, `rawTerminalStatsAgg`, and `aggregateCacheHits`. - **Tests**: `TestGetStatsMatViewCacheHitsHybrid` verifies the hybrid cache-hit path including nil and zero contracts and agreement with the raw path. `TestMatViewShapeErrorFallsBackAndSelfHeals`, `TestMatViewStaleShapeFallsBackAndSelfHeals`, and `TestFilterMatViewShapeErrorFallsBack` cover the 42P01, 42703, and filter-view drop scenarios end-to-end, including background self-heal convergence. - [x] Bug fix - [x] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI - [x] Core (Go) - [ ] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [ ] UI (React) - [ ] Docs ```sh go test ./framework/logstore/... -run TestGetStatsMatViewCacheHitsHybrid go test ./framework/logstore/... -run TestMatViewShapeErrorFallsBackAndSelfHeals go test ./framework/logstore/... -run TestMatViewStaleShapeFallsBackAndSelfHeals go test ./framework/logstore/... -run TestFilterMatViewShapeErrorFallsBack go test ./framework/logstore/... -run TestIsMatViewShapeError go test ./framework/logstore/... ``` The self-heal tests drop or replace `mv_logs_hourly` mid-run and assert that reads continue returning correct results from the raw table with no error, that `matViewsReady` is set to false immediately, and that the view is recreated with the correct shape within 90 seconds. - [x] No The new matview columns require a schema migration. On first deploy, `repairMatViewShapes` detects the missing columns, drops and recreates `mv_logs_hourly`, and the self-heal path handles any replica that reads before the rebuild completes. Closes #5384 No auth, secrets, PII, or sandboxing changes. The new SQL expressions are constants composed only of built-in PostgreSQL operators and are not user-controlled. - [ ] I read `docs/contributing/README.md` and followed the guidelines - [x] I added/updated tests where appropriate - [ ] I updated documentation where needed - [x] I verified builds succeed (Go and UI) - [ ] I verified the CI pipeline passes locally if applicable
…hq#5507) ## Summary This PR adds two improvements to the materialized view read path: cache-hit statistics are now served from the hybrid aggregate (interior buckets from `mv_logs_hourly`, boundary slivers classified raw) instead of a separate full-window raw scan, and a runtime self-heal mechanism automatically recovers from missing or stale-shaped materialized views without requiring a process restart. ## Changes - **Cache hits in the hybrid aggregate**: Three new columns (`direct_cache_hits`, `semantic_cache_hits`, `cache_debug_count`) are added to `mv_logs_hourly` using the same `cacheDebugJSONGuard` and `cacheDebugHitTypeExpr` expressions shared across the matview DDL, boundary sliver queries, and `aggregateCacheHits`. `cache_debug_count` preserves the nil contract: when no row in the window carried valid `cache_debug` JSON the fields are omitted from the response, and when cache rows exist but none were direct/semantic explicit zeros are returned. The previous approach issued a separate full-window raw scan for cache hits after the hybrid aggregate completed; that scan is removed. - **Runtime matview self-heal** (`matviewheal.go`): `isMatViewShapeError` classifies PostgreSQL error codes `42P01` (undefined table), `42703` (undefined column), and `55000` (object not in prerequisite state) as shape errors. `fallBackToRaw` is called at every matview dispatch site — on a shape error it disables the matview read path process-wide, logs a warning, and triggers a single-flight background repair via `triggerMatViewSelfHeal`. The repair runs `ensureMatViews` then `refreshMatViews` and re-enables the path on success. A 30-second cooldown (`matViewHealCooldown`) bounds repair frequency; while broken, every request continues succeeding via the raw fallback. Two new atomic fields (`matViewHealInFlight`, `matViewHealLastAttempt`) are added to `RDBLogStore`. - **Shared SQL constants**: The inline regex strings for the cache debug guard and hit-type extractor are replaced with named constants `cacheDebugJSONGuard` and `cacheDebugHitTypeExpr`, used consistently across the matview DDL, `applyFilters`, `rawTerminalStatsAgg`, and `aggregateCacheHits`. - **Tests**: `TestGetStatsMatViewCacheHitsHybrid` verifies the hybrid cache-hit path including nil and zero contracts and agreement with the raw path. `TestMatViewShapeErrorFallsBackAndSelfHeals`, `TestMatViewStaleShapeFallsBackAndSelfHeals`, and `TestFilterMatViewShapeErrorFallsBack` cover the 42P01, 42703, and filter-view drop scenarios end-to-end, including background self-heal convergence. ## Type of change - [x] Bug fix - [x] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [ ] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [ ] UI (React) - [ ] Docs ## How to test ```sh go test ./framework/logstore/... -run TestGetStatsMatViewCacheHitsHybrid go test ./framework/logstore/... -run TestMatViewShapeErrorFallsBackAndSelfHeals go test ./framework/logstore/... -run TestMatViewStaleShapeFallsBackAndSelfHeals go test ./framework/logstore/... -run TestFilterMatViewShapeErrorFallsBack go test ./framework/logstore/... -run TestIsMatViewShapeError go test ./framework/logstore/... ``` The self-heal tests drop or replace `mv_logs_hourly` mid-run and assert that reads continue returning correct results from the raw table with no error, that `matViewsReady` is set to false immediately, and that the view is recreated with the correct shape within 90 seconds. ## Breaking changes - [x] No The new matview columns require a schema migration. On first deploy, `repairMatViewShapes` detects the missing columns, drops and recreates `mv_logs_hourly`, and the self-heal path handles any replica that reads before the rebuild completes. ## Related issues Closes maximhq#5384 ## Security considerations No auth, secrets, PII, or sandboxing changes. The new SQL expressions are constants composed only of built-in PostgreSQL operators and are not user-controlled. ## Checklist - [ ] I read `docs/contributing/README.md` and followed the guidelines - [x] I added/updated tests where appropriate - [ ] I updated documentation where needed - [x] I verified builds succeed (Go and UI) - [ ] I verified the CI pipeline passes locally if applicable
…hq#5507) ## Summary This PR adds two improvements to the materialized view read path: cache-hit statistics are now served from the hybrid aggregate (interior buckets from `mv_logs_hourly`, boundary slivers classified raw) instead of a separate full-window raw scan, and a runtime self-heal mechanism automatically recovers from missing or stale-shaped materialized views without requiring a process restart. ## Changes - **Cache hits in the hybrid aggregate**: Three new columns (`direct_cache_hits`, `semantic_cache_hits`, `cache_debug_count`) are added to `mv_logs_hourly` using the same `cacheDebugJSONGuard` and `cacheDebugHitTypeExpr` expressions shared across the matview DDL, boundary sliver queries, and `aggregateCacheHits`. `cache_debug_count` preserves the nil contract: when no row in the window carried valid `cache_debug` JSON the fields are omitted from the response, and when cache rows exist but none were direct/semantic explicit zeros are returned. The previous approach issued a separate full-window raw scan for cache hits after the hybrid aggregate completed; that scan is removed. - **Runtime matview self-heal** (`matviewheal.go`): `isMatViewShapeError` classifies PostgreSQL error codes `42P01` (undefined table), `42703` (undefined column), and `55000` (object not in prerequisite state) as shape errors. `fallBackToRaw` is called at every matview dispatch site — on a shape error it disables the matview read path process-wide, logs a warning, and triggers a single-flight background repair via `triggerMatViewSelfHeal`. The repair runs `ensureMatViews` then `refreshMatViews` and re-enables the path on success. A 30-second cooldown (`matViewHealCooldown`) bounds repair frequency; while broken, every request continues succeeding via the raw fallback. Two new atomic fields (`matViewHealInFlight`, `matViewHealLastAttempt`) are added to `RDBLogStore`. - **Shared SQL constants**: The inline regex strings for the cache debug guard and hit-type extractor are replaced with named constants `cacheDebugJSONGuard` and `cacheDebugHitTypeExpr`, used consistently across the matview DDL, `applyFilters`, `rawTerminalStatsAgg`, and `aggregateCacheHits`. - **Tests**: `TestGetStatsMatViewCacheHitsHybrid` verifies the hybrid cache-hit path including nil and zero contracts and agreement with the raw path. `TestMatViewShapeErrorFallsBackAndSelfHeals`, `TestMatViewStaleShapeFallsBackAndSelfHeals`, and `TestFilterMatViewShapeErrorFallsBack` cover the 42P01, 42703, and filter-view drop scenarios end-to-end, including background self-heal convergence. ## Type of change - [x] Bug fix - [x] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [ ] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [ ] UI (React) - [ ] Docs ## How to test ```sh go test ./framework/logstore/... -run TestGetStatsMatViewCacheHitsHybrid go test ./framework/logstore/... -run TestMatViewShapeErrorFallsBackAndSelfHeals go test ./framework/logstore/... -run TestMatViewStaleShapeFallsBackAndSelfHeals go test ./framework/logstore/... -run TestFilterMatViewShapeErrorFallsBack go test ./framework/logstore/... -run TestIsMatViewShapeError go test ./framework/logstore/... ``` The self-heal tests drop or replace `mv_logs_hourly` mid-run and assert that reads continue returning correct results from the raw table with no error, that `matViewsReady` is set to false immediately, and that the view is recreated with the correct shape within 90 seconds. ## Breaking changes - [x] No The new matview columns require a schema migration. On first deploy, `repairMatViewShapes` detects the missing columns, drops and recreates `mv_logs_hourly`, and the self-heal path handles any replica that reads before the rebuild completes. ## Related issues Closes maximhq#5384 ## Security considerations No auth, secrets, PII, or sandboxing changes. The new SQL expressions are constants composed only of built-in PostgreSQL operators and are not user-controlled. ## Checklist - [ ] I read `docs/contributing/README.md` and followed the guidelines - [x] I added/updated tests where appropriate - [ ] I updated documentation where needed - [x] I verified builds succeed (Go and UI) - [ ] I verified the CI pipeline passes locally if applicable

Summary
This PR adds two improvements to the materialized view read path: cache-hit statistics are now served from the hybrid aggregate (interior buckets from
mv_logs_hourly, boundary slivers classified raw) instead of a separate full-window raw scan, and a runtime self-heal mechanism automatically recovers from missing or stale-shaped materialized views without requiring a process restart.Changes
Cache hits in the hybrid aggregate: Three new columns (
direct_cache_hits,semantic_cache_hits,cache_debug_count) are added tomv_logs_hourlyusing the samecacheDebugJSONGuardandcacheDebugHitTypeExprexpressions shared across the matview DDL, boundary sliver queries, andaggregateCacheHits.cache_debug_countpreserves the nil contract: when no row in the window carried validcache_debugJSON the fields are omitted from the response, and when cache rows exist but none were direct/semantic explicit zeros are returned. The previous approach issued a separate full-window raw scan for cache hits after the hybrid aggregate completed; that scan is removed.Runtime matview self-heal (
matviewheal.go):isMatViewShapeErrorclassifies PostgreSQL error codes42P01(undefined table),42703(undefined column), and55000(object not in prerequisite state) as shape errors.fallBackToRawis called at every matview dispatch site — on a shape error it disables the matview read path process-wide, logs a warning, and triggers a single-flight background repair viatriggerMatViewSelfHeal. The repair runsensureMatViewsthenrefreshMatViewsand re-enables the path on success. A 30-second cooldown (matViewHealCooldown) bounds repair frequency; while broken, every request continues succeeding via the raw fallback. Two new atomic fields (matViewHealInFlight,matViewHealLastAttempt) are added toRDBLogStore.Shared SQL constants: The inline regex strings for the cache debug guard and hit-type extractor are replaced with named constants
cacheDebugJSONGuardandcacheDebugHitTypeExpr, used consistently across the matview DDL,applyFilters,rawTerminalStatsAgg, andaggregateCacheHits.Tests:
TestGetStatsMatViewCacheHitsHybridverifies the hybrid cache-hit path including nil and zero contracts and agreement with the raw path.TestMatViewShapeErrorFallsBackAndSelfHeals,TestMatViewStaleShapeFallsBackAndSelfHeals, andTestFilterMatViewShapeErrorFallsBackcover the 42P01, 42703, and filter-view drop scenarios end-to-end, including background self-heal convergence.Type of change
Affected areas
How to test
The self-heal tests drop or replace
mv_logs_hourlymid-run and assert that reads continue returning correct results from the raw table with no error, thatmatViewsReadyis set to false immediately, and that the view is recreated with the correct shape within 90 seconds.Breaking changes
The new matview columns require a schema migration. On first deploy,
repairMatViewShapesdetects the missing columns, drops and recreatesmv_logs_hourly, and the self-heal path handles any replica that reads before the rebuild completes.Related issues
Closes #5384
Security considerations
No auth, secrets, PII, or sandboxing changes. The new SQL expressions are constants composed only of built-in PostgreSQL operators and are not user-controlled.
Checklist
docs/contributing/README.mdand followed the guidelines