Research Overview Tracks Agenda

Query Coalescing (In-flight Deduplication)

Status: Partial implementation Last updated: 2026-03-27

Current runtime baseline: - GET /api/v1/q/{query_id} coalesces unconditional prepared-query reads by query_id. - query.patch coalesces strict JSON patch requests sharing the same base_etag -> current_etag transition. - Metrics, auth-scoped fingerprints, cancellation semantics, and broader entry-point coverage remain follow-on work.

Goal: Reduce server load and tail latency under bursty traffic by preventing the "thundering herd" problem: when many clients request the same expensive query at once, the server should execute it once and share the result.

This is a performance feature that complements: - HTTP cache validators (ETags) (docs/ETAG_VALIDATORS.md) - prepared queries (GET /api/v1/q/{query_id}) - telemetry (docs/TELEMETRY_AND_MIGRATION.md)


1) Scope and safety

Query coalescing is only safe when the result is deterministic for a given input.

SkeinDB defines a query as coalescable iff all of the following hold:

1) It is a read-only query (no DDL/DML). 2) It has no non-deterministic functions (e.g., random(), uuid(), now() unless pinned). 3) It does not depend on mutable session state (e.g., user variables). 4) It executes under a compatible snapshot policy: - explicit snapshot timestamp (read_at.ts) - OR a stable "group snapshot" policy used only for cacheable HTTP endpoints

MySQL protocol sessions may opt into coalescing explicitly. SkeinQL cacheable GET endpoints may enable it by default.


2) Canonical query fingerprint

Coalescing groups requests by a fingerprint:

fingerprint = hash( query_id_or_signature, canonical_args, read_policy, tenant_or_auth_scope, selected_format )

Where: - query_id_or_signature is the prepared query_id (preferred) or a canonical SkeinIR signature. - canonical_args is stable (sorted keys, normalized numbers). - tenant/auth scope prevents data leakage across users. - selected_format ensures the result payload is identical.

Note: - Coalescing should treat projection differences as different fingerprints.


3) Coalescing architecture

3.1 In-flight map

Maintain a concurrent map:

Map

InFlightQuery contains: - started_at - leader_request_id - waiters count - cancellation tokens for waiters - shared execution handle (task/future) - shared result buffer (or streaming fan-out handle)

3.2 Leader / joiner protocol

On request arrival:

1) Compute fingerprint. 2) If map has an InFlightQuery: - register as joiner - await leader completion - return the same response payload 3) Otherwise: - insert new InFlightQuery as leader - execute query - publish result to joiners - remove from map

3.3 Coalescing window (optional)

A small window (e.g., 2–10 ms) can increase coalescing hit rate: - leader delays execution briefly to gather joiners - use only when latency budget allows


4) Interaction with ETags

Coalescing is most valuable when the server cannot return 304.

Recommended flow for GET /api/v1/q/{query_id}:

1) If If-None-Match is present: - compute ETag cheaply if possible (dependency-set ETag) - if match -> return 304 immediately 2) Otherwise, execute query: - coalesce in-flight executions by fingerprint - include computed ETag in response

This reduces repeated heavy query execution on cache misses and during bursts.


5) Cancellation and resource limits

5.1 Joiner cancellation

If a joiner disconnects/cancels: - remove it from waiters - do NOT cancel the leader automatically

5.2 Group cancellation

If all waiters and the leader cancel: - cancel the underlying execution if supported by the executor

5.3 Limits

Protect the server with limits: - max_inflight_groups - max_waiters_per_group - max_result_bytes_to_fanout (fallback: do not coalesce, or cache to disk)


6) Metrics

Expose: - coalesce_groups_created_total - coalesce_joiners_total - coalesce_hit_rate - coalesce_wait_ms_p50/p95 - coalesce_aborted_total - coalesce_max_waiters_observed


7) Implementation phases

Phase 1: - Coalescing for prepared queries executed via GET /api/v1/q/{query_id} - Coalescing for query.patch (strict JSON mode; many clients share one computed patch) - In-memory shared result buffer for rows_json only

Phase 2: - Coalescing for POST query.select (SkeinQL) - Optional coalescing window

Phase 3: - Safe opt-in coalescing for MySQL sessions - Streaming fan-out for large results


8) SkeinQL surfaces

Recommended settings (admin-only): - settings.set { "query_coalesce": { "enabled": true, "window_ms": 5, "max_groups": 1000 } }

Recommended stats fields (stats.snapshot): - coalescing.enabled - coalescing.groups_inflight - coalescing.hit_rate


9) Backlog

  • QC01: query fingerprint + canonicalization utilities
  • QC02: in-flight query map + leader/joiner
  • QC03: GET /api/v1/q coalescing + tests
  • QC04: metrics + dashboards