All writing

Freshness should choose the compute path

Route requests across batch, incremental, and live evidence according to measured coverage rather than a fixed data source.

The cheapest data source is often the least current, while the freshest source is usually the most expensive to query. A production insight system needs both, but a fixed preference for either one produces stale answers or unnecessary compute.

Freshness can be a routing input. Measure how much of the requested window is already materialized, then choose a full batch read, a small incremental merge, or a live reconstruction.

Represent coverage explicitly

This section describes the span and version available in each source. The router can only make a sound decision when freshness is data, not an assumption embedded in repository order.

The routing inputs and possible plans can be represented independently of storage technology:

export type TimeWindow = {
  start: string;
  end: string;
};

export type Coverage = {
  source: 'batch' | 'incremental' | 'live';
  start: string;
  end: string;
  watermark: string;
  definitionVersion: string;
};

export type QueryPlan =
  | { kind: 'batch'; window: TimeWindow }
  | { kind: 'merge'; batch: TimeWindow; live: TimeWindow }
  | { kind: 'live'; window: TimeWindow };

The definition version matters as much as the time range. Two sources that calculate a metric differently cannot be merged merely because their timestamps touch.

Coverage should come from successful materialization metadata. Using the scheduled completion time confuses intent with reality when a batch is delayed or partially failed.

Choose the smallest valid plan

This section uses deterministic routing based on requested time and compatible coverage. The planner does not query data; it returns an inspectable plan.

If materialized coverage contains the full request under the expected definition, use it. If it covers a prefix, merge that prefix with a live tail. If coverage is missing, stale beyond policy, or definition-incompatible, use the live path or return an explicit unsupported state when live work would be unsafe.

Define interval inclusivity once so the boundary event is not double-counted or dropped. Half-open windows such as [start, end) make adjacent materialized and live ranges easier to compose.

The planner also needs a policy for very small uncovered tails. A live merge may be worthwhile for a short gap but too expensive for a long one; that limit belongs in configuration and should be backed by latency and cost measurements.

Execute with one aggregation definition

This section keeps routing from changing meaning. Materialized and live readers should return the same intermediate grain, and one merge definition should produce the final result.

If the batch store contains final metric rows while the live path emits raw events, they cannot be concatenated directly. Normalize both into compatible buckets or share the aggregation definition between materialization and live computation. Definition compatibility should be checked before execution, not discovered when values disagree.

Include the selected plan and source watermarks in internal response metadata. That makes a stale-answer report diagnosable without exposing storage topology to the end user.

Protect the expensive path

This section prevents a freshness gap from causing a burst of identical live work. Coalescing lets concurrent requests share one computation while a short-lived cache absorbs immediate repeats.

The work identity should include the subject, window, definition version, and relevant watermark. In a multi-instance service, use shared coordination or accept that coalescing is local and combine it with bounded concurrency at the data source.

Backpressure belongs at this boundary too. When the expensive path is saturated, return a bounded stale result or explicit pending state according to product policy rather than creating unlimited work. Freshness routing improves cost only when the fallback path has a capacity contract.

Freshness is not just a label shown beside a result. It can select the least expensive path that still satisfies the requested evidence window, provided every path preserves the same metric meaning.