OriginChainDB docs
examples · vector · 10 / 13

10. IVF coverage and cell skew

← Vector examples

what this does

GET /v1/tenants/:t/vector/:table/ivf-rebalance-status reports the shape of an IVF index: how many vectors sit in each cell, how lopsided that distribution is, how many stored vectors have a cell posting at all, and whether the engine thinks you should retrain.

It is read-only. Nothing is rebalanced as a side effect - the recommendation is a recommendation.

when to use it

  • Your IVF queries return fewer or worse hits than you expect, and you need to know whether the index actually covers the data.
  • After a bulk ingest, to confirm the new vectors were assigned to cells rather than merely stored.
  • On a schedule, to catch cell skew building up as the corpus drifts away from the centroids you trained.

the request

GET /v1/tenants/:t/vector/:table/ivf-rebalance-status
curl "https://$OC_HOST/v1/tenants/$OC_TENANT/vector/shop.products/ivf-rebalance-status" \
  -H "Authorization: Bearer $OC_TOKEN"

what you get back

{
  "total_live":     100000,
  "partitions":     1024,
  "live_per_cell":  [112, 96, 143, 87 /* ... one entry per cell ... */],
  "skew":           1.46,
  "action":         "none",
  "stored_vectors": 100000,
  "unindexed":      0,
  "coverage":       1.0
}

action is one of "none", "recommended" or "required", set from skew against the engine's thresholds.

response fields

FieldRequiredNotes
total_live-Vectors with a cell posting, summed across every cell.
partitions-Installed centroid count. Equals the length of live_per_cell.
live_per_cell-Posting count per cell, indexed by cell id. A cell that was never written reports 0.
skew-max(live_per_cell) / mean(live_per_cell), or 0.0 on an empty corpus. A perfectly even index sits at 1.0.
action-"recommended" above a skew of 2.0, "required" above 5.0, otherwise "none".
stored_vectors-Vectors stored under this table, counted from the keys, whether or not they are cell-assigned.
unindexed-stored_vectors - total_live. Vectors with no cell posting: written before the centroids were installed, or left behind by a skipped backfill.
coverage-total_live / stored_vectors, clamped to 1.0. This is the number to alert on.

how it works

The report walks each cell's posting list and counts entries; it does not decode the payloads behind them. That makes it cheap enough to poll, and it means "live" is a posting count rather than a verified row count - an index with stale postings from an interrupted re-install can over-report total_live. Re-training and re-installing is the cleanup, not a per-call scan.

stored_vectors, unindexed and coverage exist for one failure shape that every other field hides: a table with ten thousand vectors stored and zero of them indexed. Nothing else on this response raises a flag there - every cell is empty, so skew is 0.0 and action is "none" - while every IVF query against that table returns nothing.

Skew matters for latency rather than correctness. A query probes a fixed number of cells; if one cell holds ten times its share, the queries that land on it do ten times the work. That is what the action hint is tracking.

common mistakes

  • Reading total_live alone. It cannot distinguish "the index is small" from "the index is empty and the data is elsewhere". Read coverage with it, every time.
  • Calling it on an HNSW table. There are no centroids, so the route answers 503 with "error": "ivf_centroids_not_installed". That is the same refusal an IVF query gets, and it means the same thing.
  • Waiting for a rebalance to happen. Nothing is automatic. "action": "required" is the engine telling you to call train-and-install-centroids yourself.
  • Treating a low coverage as a query-tuning problem. No nprobe setting reaches a vector that has no posting. Re-install the centroids so the backfill runs.