RippleDB
RippleDB
InternalArchitecture

Performance via Batching

Solve N+1 by batching reads per tick/RAF without coupling to UI frameworks.

13. Batching (performance without magic)

13.1 The problem

Normalized access patterns cause many reads:

getInquiry(id);
getCustomer(customerId);
getAssignee(assigneeId);

Naively, this is N+1 DB queries.

13.2 The solution: batch loader

We batch reads, not invalidations.

13.3 Batch loader contract

interface BatchLoader<K, V> {
  load(key: K): Promise<V | null>;
}

Behavior:

  • collects keys during a tick / RAF
  • deduplicates
  • executes one WHERE id IN (...)
  • splits results back to callers

13.4 Critical property

The batch loader:

  • does not know about TanStack Query
  • does not know about UI
  • lives in the data access layer

Any caller benefits.

13.5 Implementation

@rippledb/client-controllers provides a reference implementation of the batch loader pattern on top of the Store interface. It automatically uses Store.getRows() when available (optimized path) or falls back to parallel getRow() calls.

The client-controllers package also provides entity controllers that combine batch loading with CRUD operations, making it easy to build data access layers that benefit from automatic batching.

On this page