Warming Strategies (v2, Beta)

This guide explains how to keep a v2 database responsive after writes, using the atomic operations on POST /api/v2/data/_modify.

Why warming matters

After write operations, the first query against modified data can experience a latency spike. This happens because:

  1. Index / segment loading โ€” new segments are memory-mapped and read on first touch.
  2. Inference cache population โ€” a v2 predict fits AdaptivePriors over the collection (and, by default, the group-formation re-expression) the first time it runs against a new state. That fit is then cached on the collection state and reused by every later query, so only the first query pays for it.
  3. Cross-table linkage โ€” a query that traverses a link builds the reverse-link index on first use.

No disk I/O happens during query execution โ€” it's this initial loading and cache population that causes the delay. The warm operation pre-executes queries so these caches are populated before real users hit them.

Note: the severity depends on data size and feature-space complexity. Small collections with simple schemas may not need warming at all; large collections with many features and links can see significant first-query delays. Measure your case before adding warming.

Single-table pattern

The simplest pattern: optimize and warm a collection immediately after modifications, in one atomic _modify call.

POST /api/v2/data/_modify
{
  "operations": [
    { "optimize": "products" },
    { "warm": [
      { "from": "products", "predict": "category" },
      { "from": "products", "recommend": "related_products" }
    ] }
  ]
}

Trade-offs:

  • Simple to implement.
  • Write latency includes the warming time.
  • Best for low-write-frequency scenarios.

Write/read separation pattern

For high-frequency writes, separate the write and read collections:

  1. Write to a dedicated write collection.
  2. Periodically optimize, warm, and copy it into the read collection.
  3. Queries always hit the pre-warmed read collection.
POST /api/v2/data/_modify
{
  "operations": [
    { "optimize": "products_write" },
    { "warm": [
      { "from": "products_write", "predict": "category" },
      { "from": "products_write", "recommend": "related_products" },
      { "from": "products_write", "where": { "popular": true }, "predict": "price_range" }
    ] },
    { "from": "products_write", "copy": "products", "replace": true }
  ]
}

Trade-offs:

  • Writes are fast (no warming delay on the hot path).
  • Read queries always hit warmed data.
  • There is a data-staleness window between syncs.
  • Requires a periodic sync job.

When to use optimize

The optimize operation is optional. v2 defers the segment merge off the write path (writes append a segment and are queried correctly via merge-on-read), so a collection accumulates segments as you write. optimize consolidates them into one, which improves query speed โ€” but optimize itself is a slower operation.

Optimize when:

  • You have accumulated many small writes.
  • Query performance is degrading over time.
  • You're doing a periodic sync in the write/read separation pattern.

Skip optimize when:

  • You need fast write-then-query turnaround.
  • The collection has few segments (recent writes only).
  • Write latency matters more than query latency.

Without optimize, warming still works โ€” it just warms the current (unmerged) collection state.

Choosing queries to warm

Not all queries benefit equally:

Good candidates:

  • predict / recommend / relate โ€” these fit and cache the inference model (including the group re-expression).
  • Queries that traverse links (processor.department, company.size) โ€” they build the reverse-link index.
  • $nearest / vector queries โ€” these scan the vector store.
  • Complex where clauses with multiple conditions.

May not need warming:

  • Plain _query searches โ€” already fast.
  • Queries with limit: 1 โ€” minimal computation.

Warm the shape you serve. The inference cache keys on the collection state, so any predict on a table warms the shared fit; but link traversal and candidate scoping are query-specific, so warm queries whose from/where/predict match what your users actually run.

Performance considerations

  1. Warm the queries your users actually run โ€” profile production query patterns and warm those.
  2. Don't over-warm โ€” warming too many queries increases sync time and may warm caches evicted before use.
  3. Balance freshness vs latency โ€” shorter sync intervals mean fresher data but more warming overhead.
  4. Monitor first-query latency โ€” track P99 to verify warming is effective. Warming is best-effort: a failed warm query is logged and skipped, never fatal to the _modify.