Skip to content

feat: add hybrid mode and other improvements#5

Merged
quolpr merged 4 commits into
mainfrom
quolpr/hybrid-mode
Jun 25, 2026
Merged

feat: add hybrid mode and other improvements#5
quolpr merged 4 commits into
mainfrom
quolpr/hybrid-mode

Conversation

@quolpr

@quolpr quolpr commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Added HybridDB support with faster cache-first reads and trace labels showing whether data came from cache or persistent storage.
    • Updated the devtools UI to keep the details tab selected when switching traces, making comparisons easier.
    • Added a new driver option for the hybrid runtime in the demo app.
  • Bug Fixes

    • Async selector behavior now preserves the synchronous fast path when results are immediately available.
    • Trace and call-tree badges now better reflect data source information for hybrid reads.

@codesandbox

codesandbox Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review or Edit in CodeSandbox

Open the branch in Web EditorVS CodeInsiders

Open Preview

@netlify

netlify Bot commented Jun 24, 2026

Copy link
Copy Markdown

Deploy Preview for hyperdb canceled.

Name Link
🔨 Latest commit 1b75f89
🔍 Latest deploy log https://app.netlify.com/projects/hyperdb/deploys/6a3d059f5118300007911bdc

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

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

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ 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.

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 credits.

🚦 How do rate 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 see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d4faf005-cbcd-4170-95fc-01e9a850ce73

📥 Commits

Reviewing files that changed from the base of the PR and between 1344029 and 1b75f89.

📒 Files selected for processing (10)
  • README.md
  • TODO.md
  • packages/hyperdb-demo/index.html
  • packages/hyperdb-devtool/src/components.test.tsx
  • packages/hyperdb-doc/src/content/docs/runtime/db.md
  • packages/hyperdb-doc/src/styles/theme.css
  • packages/hyperdb/src/hyperdb/runtime/hybrid-db-intervals.test.ts
  • packages/hyperdb/src/hyperdb/runtime/hybrid-db-intervals.ts
  • packages/hyperdb/src/hyperdb/runtime/hybrid-db.test.ts
  • packages/hyperdb/src/hyperdb/runtime/hybrid-db.ts
📝 Walkthrough

Walkthrough

Introduces HybridDB, a two-tier runtime combining a persistent primary DB with an in-memory cache. Adds interval-based cache tracking (hybrid-db-intervals.ts), the HybridDB/HybridDBTx classes, a new execMaybeAsync executor, runSelectorMaybeAsync selector helper, useAsyncSelector refactored to apply synchronous results immediately, SelectScanSource/CurrentSelectEventTrait tracing, devtools source badge and persistent tab state, a demo idb-hybrid mode, and documentation.

Changes

HybridDB Runtime and Supporting Infrastructure

Layer / File(s) Summary
execMaybeAsync: sync-or-async executor primitive
packages/hyperdb/src/hyperdb/core/executor.ts, packages/hyperdb/src/hyperdb/core/executor.test.ts
Adds isPromiseLike guard, internal execAsyncFrom async continuation, and exported execMaybeAsync that runs generators synchronously until a promise-like unwrap is encountered, then delegates to execAsyncFrom. Replaces the prior execAsync export. Tests cover both sync and deferred paths.
Tracer: SelectScanSource, SelectCommandEvent.source, CurrentSelectEventTrait
packages/hyperdb/src/hyperdb/core/tracer.ts, packages/hyperdb/src/hyperdb/tracing/context.ts, packages/hyperdb/src/hyperdb/tracing/store.ts, packages/hyperdb/src/hyperdb/commands/runner.ts
Adds SelectScanSource union and optional source field to SelectCommandEvent. Introduces CurrentSelectEventTrait type, constructor, type-guard, DB/trait reader helpers, and withCurrentSelectEventTrait. Re-exports propagated through tracing modules. Command runner wraps scopedDB with withCurrentSelectEventTrait before intervalScan when a selectEvent is active.
Interval caching engine: hybrid-db-intervals
packages/hyperdb/src/hyperdb/runtime/hybrid-db-intervals.ts, packages/hyperdb/src/hyperdb/runtime/hybrid-db-intervals.test.ts
Defines NormalizedInterval, IntervalTarget, HybridIntervalCache types and factory. Implements interval construction from WhereClauses (with id tie-break), empty-interval detection, merge, overlap, subtraction, limited-row coverage derivation, and canServeLimitedResultFromCache. The hybridIntervalScan generator orchestrates cache-hit vs. primary-scan decisions, upserts rows, updates coverage, and sets SelectCommandEvent.source. Full unit and edge-case tests.
HybridDB/HybridDBTx classes, SubscribableDB trait delegation, barrel export
packages/hyperdb/src/hyperdb/runtime/hybrid-db.ts, packages/hyperdb/src/hyperdb/runtime/hybrid-db.test.ts, packages/hyperdb/src/hyperdb/runtime/subscribable-db.ts, packages/hyperdb/src/hyperdb/index.ts
HybridDB wires primary/cache backends with shared interval state, delegates metadata, clears intervals on loadTables, and forwards CRUD to both tiers. HybridDBTx coordinates nested transactions, publishes cache coverage to the outer HybridDB only at final commit, and blocks operations after commit/rollback. SubscribableDB gains delegateDB() to apply traits before beginTx and intervalScan. Barrel export added. Tests cover caching, trace sources, write-through, transactions, and rollback.
runSelectorMaybeAsync and useAsyncSelector refactor
packages/hyperdb/src/hyperdb/commands/selector/selector.ts, packages/hyperdb/src/react/hooks.ts, packages/hyperdb/src/react/hooks.test.ts
Exports runSelectorMaybeAsync using execMaybeAsync, pruning childMemo sync or in .then. useAsyncSelector rewritten from async/await to a non-async run function: synchronous selector results apply immediately; promise results resolve via then/catch/finally with isRunning/rerunRequested/cancelled coalescing. Tests updated to mock/assert runSelectorMaybeAsync, including a new same-tick synchronous application test.
Devtools: source badge, tab persistence, and tests
packages/hyperdb-devtool/src/components.tsx, packages/hyperdb-devtool/src/components.test.tsx
Adds TraceDetailsTab union, "source" tone to SpanElement and CallTreeBadgeTone, color entry in treeBadgeColor, and getCallTreeOperationBadges emitting a source badge when event.source is set. Refactors TraceDetails to controlled tab/onTabChange props; DevtoolsPanelInner holds selectedTraceTab state and passes it to both render sites. Tests verify tab persistence across trace switches and source badge presence/absence.
Demo idb-hybrid mode, docs, and READMEs
packages/hyperdb-demo/src/store-mode.ts, packages/hyperdb-demo/src/stores.ts, packages/hyperdb-demo/src/BenchmarkApp.tsx, packages/hyperdb-doc/src/content/docs/runtime/db.md, packages/hyperdb-doc/src/content/docs/integrations/..., packages/hyperdb/README.md, README.md, TODO.md, packages/hyperdb-devtool/README.md
Demo app adds idb-hybrid to StoreMode, dropdown, storage-status branch, and an initStore branch constructing HybridDB. Docs add a HybridDB section in db.md, a useAsyncSelector sync-start note in react.md, and tracing tab/source notes in devtools.md. READMEs updated for HybridDB feature bullets and entry-point table.

Sequence Diagram(s)

sequenceDiagram
  participant useAsyncSelector
  participant runSelectorMaybeAsync
  participant HybridDB
  participant hybridIntervalScan
  participant CacheDB
  participant PrimaryDB

  rect rgba(100, 149, 237, 0.5)
    note over useAsyncSelector,runSelectorMaybeAsync: Synchronous-first selector path
    useAsyncSelector->>runSelectorMaybeAsync: run selector generator
    runSelectorMaybeAsync->>HybridDB: SelectRangeCmd → intervalScan
    HybridDB->>hybridIntervalScan: primary, cache, cachedIntervals, selectEvent
  end

  alt cache hit (in-mem)
    hybridIntervalScan->>CacheDB: intervalScan
    CacheDB-->>hybridIntervalScan: rows (source = "in-mem")
    hybridIntervalScan-->>runSelectorMaybeAsync: rows (sync)
    runSelectorMaybeAsync-->>useAsyncSelector: result (sync, applied same tick)
  else cache miss (persist)
    hybridIntervalScan->>PrimaryDB: intervalScan
    PrimaryDB-->>hybridIntervalScan: rows (source = "persist")
    hybridIntervalScan->>CacheDB: upsert rows + merge coverage
    hybridIntervalScan-->>runSelectorMaybeAsync: rows (may be Promise)
    runSelectorMaybeAsync-->>useAsyncSelector: Promise
    useAsyncSelector->>useAsyncSelector: then/catch/finally → setResult
  end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Poem

🐇 A bunny digs two tunnels deep,
One in memory, one to keep —
On cache miss, it hops to disk,
Stores the range (no need to frisk).
Sync if fast, async when slow,
The hybrid path puts on a show! 🌟

🚥 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 is concise and accurately reflects the main hybrid mode work, even though it also mentions broader improvements.
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 quolpr/hybrid-mode

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.

@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: 6

🤖 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/hyperdb-devtool/src/components.test.tsx`:
- Around line 181-252: The tab-persistence test in HyperDBDevtoolsPanel can leak
mounted DOM state if an assertion fails before teardown. Update the test around
createRoot, root.render, and the host element so cleanup always runs by wrapping
the render/assertions in a try/finally or equivalent shared teardown. Ensure
root.unmount() and host.remove() are guaranteed even when the selections in
getDetailTab or getButtonContaining fail.

In `@packages/hyperdb/src/hyperdb/runtime/hybrid-db-intervals.test.ts`:
- Line 1: The test file formatting is out of sync with Prettier. Run Prettier on
the hybrid-db-intervals test and commit the formatting-only changes, keeping the
existing Vitest imports and test structure intact.

In `@packages/hyperdb/src/hyperdb/runtime/hybrid-db-intervals.ts`:
- Line 1: This file only needs formatting cleanup to satisfy the CI prettier
check. Run the formatter on the hybrid-db-intervals.ts module and ensure the
import statement for DBCmd matches the project’s Prettier style; no functional
changes are needed.

In `@packages/hyperdb/src/hyperdb/runtime/hybrid-db.ts`:
- Around line 1-3: This file is failing the formatting check, so update the
imports in hybrid-db.ts to match Prettier’s expected style. Reformat the
existing import statements from the module paths under ../commands/async,
../core/contracts, and ../core/primitives, then run the formatter so the file
matches the project’s standard output.
- Around line 122-144: The write-through paths in hybrid-db’s insert, upsert,
and delete currently commit the primary before the cache, leaving a failure
window where the primary succeeds but the cache can still serve stale data.
Update the HybridDB mutation flow so primary and cache are coordinated
transactionally, or ensure a cache invalidation/rebuild happens before surfacing
any cache-write error. Apply the same fix to the matching mutation methods
referenced by the same pattern elsewhere in HybridDB.

In `@packages/hyperdb/src/react/hooks.test.ts`:
- Line 290: The test still asserts the old runSelectorAsync mock, but the hook
wiring now uses runSelectorMaybeAsync instead. Update this expectation in
hooks.test.ts to assert the behavior of runSelectorMaybeAsync in the relevant
test case, or remove the stale assertion if it is no longer needed, using the
runSelectorMaybeAsync and runSelectorAsync mock names to locate the affected
test.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 14921998-84ad-4de1-9f88-86ba66f9b757

📥 Commits

Reviewing files that changed from the base of the PR and between edb4882 and 1344029.

📒 Files selected for processing (27)
  • README.md
  • TODO.md
  • packages/hyperdb-demo/src/BenchmarkApp.tsx
  • packages/hyperdb-demo/src/store-mode.ts
  • packages/hyperdb-demo/src/stores.ts
  • packages/hyperdb-devtool/README.md
  • packages/hyperdb-devtool/src/components.test.tsx
  • packages/hyperdb-devtool/src/components.tsx
  • packages/hyperdb-doc/src/content/docs/integrations/devtools.md
  • packages/hyperdb-doc/src/content/docs/integrations/react.md
  • packages/hyperdb-doc/src/content/docs/runtime/db.md
  • packages/hyperdb/README.md
  • packages/hyperdb/src/hyperdb/commands/runner.ts
  • packages/hyperdb/src/hyperdb/commands/selector/selector.ts
  • packages/hyperdb/src/hyperdb/core/executor.test.ts
  • packages/hyperdb/src/hyperdb/core/executor.ts
  • packages/hyperdb/src/hyperdb/core/tracer.ts
  • packages/hyperdb/src/hyperdb/index.ts
  • packages/hyperdb/src/hyperdb/runtime/hybrid-db-intervals.test.ts
  • packages/hyperdb/src/hyperdb/runtime/hybrid-db-intervals.ts
  • packages/hyperdb/src/hyperdb/runtime/hybrid-db.test.ts
  • packages/hyperdb/src/hyperdb/runtime/hybrid-db.ts
  • packages/hyperdb/src/hyperdb/runtime/subscribable-db.ts
  • packages/hyperdb/src/hyperdb/tracing/context.ts
  • packages/hyperdb/src/hyperdb/tracing/store.ts
  • packages/hyperdb/src/react/hooks.test.ts
  • packages/hyperdb/src/react/hooks.ts

Comment thread packages/hyperdb-devtool/src/components.test.tsx
Comment thread packages/hyperdb/src/hyperdb/runtime/hybrid-db-intervals.test.ts
Comment thread packages/hyperdb/src/hyperdb/runtime/hybrid-db-intervals.ts
Comment thread packages/hyperdb/src/hyperdb/runtime/hybrid-db.ts Outdated
Comment thread packages/hyperdb/src/hyperdb/runtime/hybrid-db.ts

expect(selector).toHaveBeenCalledWith({ projectId: "project-1" });
expect(mocks.runSelectorMaybeAsync).toHaveBeenCalledTimes(1);
expect(mocks.runSelectorAsync).not.toHaveBeenCalled();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Remove the stale runSelectorAsync assertion.

Line 100 wires runSelectorMaybeAsync, so this assertion either fails if the old mock was removed or verifies an unused mock if retained. Assert runSelectorMaybeAsync behavior here or delete this line.

🤖 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/hyperdb/src/react/hooks.test.ts` at line 290, The test still asserts
the old runSelectorAsync mock, but the hook wiring now uses
runSelectorMaybeAsync instead. Update this expectation in hooks.test.ts to
assert the behavior of runSelectorMaybeAsync in the relevant test case, or
remove the stale assertion if it is no longer needed, using the
runSelectorMaybeAsync and runSelectorAsync mock names to locate the affected
test.

@quolpr quolpr merged commit 78ce2f6 into main Jun 25, 2026
8 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