Skip to content

Improve realtime cache invalidation and reconnect handling - #55

Merged
cobraprojects merged 3 commits into
mainfrom
optimize-realtime
Jun 21, 2026
Merged

Improve realtime cache invalidation and reconnect handling#55
cobraprojects merged 3 commits into
mainfrom
optimize-realtime

Conversation

@cobraprojects

@cobraprojects cobraprojects commented Jun 21, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features
    • Added predicate-level dependency tracking to improve query-cache invalidation precision.
  • Bug Fixes
    • Realtime now warns once (instead of escalating) when the transport is unavailable, and avoids redundant snapshot notifications when data is semantically unchanged.
    • Predicate-aware invalidation reduces unnecessary realtime subscription refreshes during batched writes.
  • Tests
    • Added/expanded realtime and query-cache coverage, including predicate subscription refresh rules and snapshot deduplication.
    • Enhanced in-memory SQL test support for additional WHERE placeholder styles and UPDATE.
  • Documentation
    • Updated agent docs-based workflow guidance text and adjusted related CLI test expectations.

@netlify

netlify Bot commented Jun 21, 2026

Copy link
Copy Markdown

Deploy Preview for holo-docs canceled.

Name Link
🔨 Latest commit 4022110
🔍 Latest deploy log https://app.netlify.com/projects/holo-docs/deploys/6a375426e842340008714958

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c52dd701-ac5d-4d50-91b4-3bfc4c82df94

📥 Commits

Reviewing files that changed from the base of the PR and between 322ab7c and 4022110.

📒 Files selected for processing (6)
  • packages/db/src/cache.ts
  • packages/db/tests/query-cache.test.ts
  • packages/realtime/src/client.ts
  • packages/realtime/src/runtime.ts
  • packages/realtime/tests/realtime.client.test.ts
  • packages/realtime/tests/realtime.test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/db/tests/query-cache.test.ts
  • packages/realtime/tests/realtime.client.test.ts
  • packages/realtime/tests/realtime.test.ts
  • packages/realtime/src/client.ts
  • packages/realtime/src/runtime.ts

📝 Walkthrough

Walkthrough

This PR adds predicate-level cache dependency keys to the DB cache layer, threads normalized update values through TableQueryBuilder.update() and inferAutomaticQueryCacheInvalidationDependencies, and implements contradiction-aware subscription filtering in the realtime runtime to skip refreshes when batched invalidation events contradict a subscription's predicates. The realtime client gains transport-availability error classification and snapshot deduplication. A CLI agent-skills prompt is also tightened.

Changes

Predicate-aware cache invalidation and realtime subscription contradiction

Layer / File(s) Summary
Predicate dependency key generation and collection helpers
packages/db/src/cache.ts
Exports createTablePredicateCacheDependency and an internal exact variant; adds recursive AST-walker and record-based collector helpers to build frozen sets of predicate cache-dependency strings; exposes the export via queryCacheInternals.
Inference function updates and TableQueryBuilder wiring
packages/db/src/cache.ts, packages/db/src/query/TableQueryBuilder.ts
inferAutomaticQueryCacheDependencies now returns predicate-derived dependencies; inferAutomaticQueryCacheInvalidationDependencies gains a values parameter and appends predicate dependencies from both query AST and provided values; insert inference appends row-derived predicate dependencies; TableQueryBuilder.update() normalizes values once and passes them into invalidateMutationQueries.
DB cache and query-cache test updates
packages/db/tests/query-cache.test.ts
Adjusts existing test assertions to reflect new predicate dependency keys, introduces a test for exact record predicate invalidation, loosens transaction-deferred invalidation matchers, and extends the normalization test with a posts-table predicate plan verifying db:main:posts:where:user_id:1.
Realtime runtime batch event tracking and contradiction logic
packages/realtime/src/runtime.ts
Extends RuntimeState with refreshGroups, PendingInvalidationBatch with an events array, and ActiveSubscription with predicateDependencies; introduces predicate-parsing helpers and types; implements isSubscriptionContradictedByInvalidation; adds refresh-group management functions; and updates handleDatabaseInvalidation to filter contradicted subscriptions using the full batched event set.
Realtime subscription predicate tests and memory adapter UPDATE support
packages/realtime/tests/realtime.test.ts
Adds UPDATE SQL parsing to RelationalMemoryAdapter.execute(), fixes unindexed ? binding in filterMemoryRows(), and introduces four predicate-subscription test cases covering contradiction skip, batched freshness, and row-moves in/out of predicate result sets.

Realtime client transport error handling and snapshot deduplication

Layer / File(s) Summary
Transport availability error handler and snapshot dedup
packages/realtime/src/client.ts
Adds isRealtimeTransportAvailabilityError and handleRealtimeConnectionError (warn-once, bypasses handleError for availability errors); introduces snapshotDataHash to skip redundant notify() calls; updates connect() error callbacks; exports new helpers via realtimeClientInternals.
Realtime client tests: snapshot dedup and availability error suppression
packages/realtime/tests/realtime.client.test.ts
Adds a test verifying listeners are not called on semantically identical snapshots; updates an existing failure-handling test to track subscription attempts and assert console warnings; and adds a test confirming handleError is not invoked for unavailable-transport errors while emitting a console warning.
Next.js adapter: no reconnect on unrelated rerenders
packages/adapter-next/tests/client.test.ts
Adds a test that simulates multiple listPosts() calls during unrelated React rerenders and asserts the realtime transport's query and subscribe are each invoked only once.

CLI agent-skills prompt update

Layer / File(s) Summary
Agent-skills prompt wording and test assertion
packages/cli/src/agent-skills.ts, packages/cli/tests/cli.test.ts
Rewrites the required-workflow doc-lookup steps with tightened constraints, adds explicit conditions for package-inspection, updates the discrepancy instruction, and adjusts the cli test expectation to match the new "then act" suffix.

Sequence Diagram(s)

sequenceDiagram
  participant update as TableQueryBuilder.update()
  participant cache as inferAutomaticQueryCacheInvalidationDependencies
  participant runtime as handleDatabaseInvalidation
  participant contradiction as isSubscriptionContradictedByInvalidation
  participant subscription as RealtimeSubscription

  update->>cache: plan, connectionName, normalizedValues
  cache-->>update: [table dep, where:col:val, where-exact:col:val, ...]
  update->>runtime: DatabaseDependencyInvalidationEvent (batch)
  runtime->>contradiction: subscription deps vs batch events
  contradiction-->>runtime: contradicted? (skip / refresh)
  runtime->>subscription: refresh (if not contradicted)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • cobraprojects/holo-js#51: Introduced the foundational createRealtimeQueryStore and realtime client/transport infrastructure that this PR extends with snapshot deduplication and transport-availability error handling.

Poem

🐇 Hop, hop, the cache is keen,
Predicates now filter the scene!
Contradictions caught mid-flight,
Snapshots hashed to skip the blight.
The runtime knows which rows have changed—
No needless queries rearranged! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main objectives of the changeset, which focuses on improving realtime cache invalidation handling and reconnection behavior across multiple packages.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch optimize-realtime

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/realtime/src/client.ts (1)

613-625: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Synchronous subscribe errors can leave the store stuck in a false-connected state.

If transport.subscribe(...) invokes onError synchronously, Line 624 overwrites the connected = false set in the callback. That can block recovery because future connect() calls return early on connected === true.

Suggested fix
+        let subscriptionFailedDuringSetup = false
         unsubscribe = transport.subscribe<TResult>(name, args, (nextSnapshot) => {
           if (startupId !== currentStartupId) {
             return
           }

           seenLiveSnapshot = true
           setSnapshot(nextSnapshot)
         }, (error) => {
+          subscriptionFailedDuringSetup = true
           connected = false
           handleRealtimeConnectionError(error)
         })
-        connected = true
+        connected = !subscriptionFailedDuringSetup
🤖 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 `@packages/realtime/src/client.ts` around lines 613 - 625, The issue is that
setting `connected = true` on line 624 can overwrite a synchronous `connected =
false` assignment that occurs in the error callback (lines 619-622). If
transport.subscribe() invokes its onError callback synchronously, the error
handler sets connected to false, but then line 624 immediately overwrites it to
true, leaving the store in an incorrect connected state. Move the `connected =
true` assignment to before the transport.subscribe() call so that any
synchronous error callbacks will correctly override the true state with false,
allowing proper error recovery on subsequent connect() attempts.
🧹 Nitpick comments (1)
packages/realtime/tests/realtime.test.ts (1)

186-212: 💤 Low value

Add a guard for unexpected assignment format.

If the assignment regex fails to match (e.g., an unexpected SQL format), column becomes undefined, silently corrupting test data by setting row[undefined]. While this is test-only code, an explicit guard would make test failures easier to diagnose.

🔧 Suggested improvement
 const assignments = rawAssignments!.split(', ').map((assignment, index) => {
   const [, column, rawPlaceholder] = assignment.match(/^"([^"]+)" = \?(\d*)$/) ?? []
+  if (!column) {
+    throw new Error(`RelationalMemoryAdapter: Unexpected assignment format: ${assignment}`)
+  }
   const bindingIndex = rawPlaceholder ? Number(rawPlaceholder) - 1 : index
-  return { column: column!, value: bindings[bindingIndex] }
+  return { column, value: bindings[bindingIndex] }
 })
🤖 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 `@packages/realtime/tests/realtime.test.ts` around lines 186 - 212, In the
assignments parsing section where rawAssignments is split and mapped, add a
guard to handle cases where the assignment regex fails to match. After
performing the regex match in the map function over the assignments, check that
the match actually succeeded before destructuring the result. If the match is
null or the column is undefined, either filter out that assignment or throw an
error with a descriptive message about the unexpected SQL format. This will
prevent silently corrupting test data by setting row[undefined] and make test
failures easier to diagnose.
🤖 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 `@packages/db/src/cache.ts`:
- Around line 507-509: The code collects both exact and non-exact predicate
dependencies from plan.predicates (lines 507-508) but only collects non-exact
dependencies from the values record (line 509). Add a call to collect exact
predicate dependencies from values to maintain consistency with how
inferAutomaticInsertCacheInvalidationDependencies handles row records (lines
527-532). After the collectRecordPredicateDependencies call for values, add
another dependencies.push statement that collects exact record predicate
dependencies from the values record using the same connectionName and tableName
parameters.

---

Outside diff comments:
In `@packages/realtime/src/client.ts`:
- Around line 613-625: The issue is that setting `connected = true` on line 624
can overwrite a synchronous `connected = false` assignment that occurs in the
error callback (lines 619-622). If transport.subscribe() invokes its onError
callback synchronously, the error handler sets connected to false, but then line
624 immediately overwrites it to true, leaving the store in an incorrect
connected state. Move the `connected = true` assignment to before the
transport.subscribe() call so that any synchronous error callbacks will
correctly override the true state with false, allowing proper error recovery on
subsequent connect() attempts.

---

Nitpick comments:
In `@packages/realtime/tests/realtime.test.ts`:
- Around line 186-212: In the assignments parsing section where rawAssignments
is split and mapped, add a guard to handle cases where the assignment regex
fails to match. After performing the regex match in the map function over the
assignments, check that the match actually succeeded before destructuring the
result. If the match is null or the column is undefined, either filter out that
assignment or throw an error with a descriptive message about the unexpected SQL
format. This will prevent silently corrupting test data by setting
row[undefined] and make test failures easier to diagnose.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5d9a2c2f-4d3e-48fc-b02a-83f08bb465e5

📥 Commits

Reviewing files that changed from the base of the PR and between 03ac1fe and 322ab7c.

📒 Files selected for processing (10)
  • packages/adapter-next/tests/client.test.ts
  • packages/cli/src/agent-skills.ts
  • packages/cli/tests/cli.test.ts
  • packages/db/src/cache.ts
  • packages/db/src/query/TableQueryBuilder.ts
  • packages/db/tests/query-cache.test.ts
  • packages/realtime/src/client.ts
  • packages/realtime/src/runtime.ts
  • packages/realtime/tests/realtime.client.test.ts
  • packages/realtime/tests/realtime.test.ts

Comment thread packages/db/src/cache.ts
@cobraprojects
cobraprojects merged commit 518eb8b into main Jun 21, 2026
5 checks passed
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