Distributed Vector Search — Configuration Reference

Settings, flags and defaults for Distributed Vector Search: the controller in the master leader, the weed-vector-worker engines, and the query router in every S3 gateway.

Controller ([master.vector] in master.toml)

The controller runs in whichever master leads. It reads [master.vector] from that master’s own master.toml, so keep the section identical on every master. An invalid value stops the master at startup.

Key Default Meaning
min_width 1 Smallest W: engines per replica group.
max_fanout 32 Largest W. No query touches more engines than this.
max_scan_mb 64 W grows until an even split leaves each engine at most this many MiB of the index to scan per query, as long as min_replicas groups still fit the engines. 0 sizes W by memory only.
min_replicas 2 Fewest replica groups per index.
max_replicas 16 Most replica groups per index.
qps_per_group 200 Queries per second one group serves before R grows.
replica_cooldown 10m R falls only after the load has asked for fewer groups this long.
warmup_timeout 10m Queries move to a new placement after this long even if its engines are not warm yet.
restart_grace 2m An engine whose session is lost keeps its slots this long.
idle_timeout 1h An index with no subscribers and no stats this long is forgotten. Its next stats report brings it back.
stats_ttl 2m A gateway’s last stats report counts until it is this old.
leader_settle 10s A new leader waits this long for engines to rejoin before it places anything.
warmups_per_engine 4 Slots one engine warms at the same time.
warmups_per_cluster 256 Slots warming at the same time across all engines.

Rules: 1 ≤ min_width ≤ max_fanout, 1 ≤ min_replicas ≤ max_replicas, max_scan_mb ≥ 0, qps_per_group > 0, and positive warm-up limits and durations. replica_cooldown and leader_settle may be 0.

Per-index bounds

[[master.vector.index]]
key = "s3://bucket/vector/index"   # the index's table URI
min_width = 4
max_width = 8
min_replicas = 2
max_replicas = 4

key is required. An S3 Vectors index’s table URI is s3://<vector bucket>/vector/<index name>. Each bound is optional, must be a positive integer, and replaces the cluster default for that index. A minimum may not exceed its maximum, and max_fanout still caps max_width.

Sizing

On every tick, for each index:

  • resident: the largest index size reported by a gateway within stats_ttl: its segments plus its unsegmented tail.
  • budget: the median --capacity-bytes of the engines that are not draining.
  • E: the number of engines that are not draining.
W_mem  = clamp(ceil(resident / (0.8 × budget)), min_width, max_width)
W_scan = clamp(max(W_mem, ceil(resident / max_scan_mb)), min_width, max_width)
W      = max(W_mem, min(W_scan, floor(E / min_replicas)))
R      = clamp(ceil(peak QPS / qps_per_group), min_replicas, max_replicas)

Here max_width is the per-index bound, capped by max_fanout. R rises at once and falls to the peak seen during replica_cooldown. When R × W is more than E, the later groups get fewer engines or are dropped.

Placement

  • Level 1, controller only. Each index gets R groups of up to W engines. Zones are ordered per index by hash, and group g takes zone g mod (number of zones). Within a zone, engines are ranked by weighted rendezvous score -w / ln(h), where w is the engine’s capacity. A group fills from its own zone first, then from any zone. Each group ranks engines by its own key, so an engine joining or leaving moves few others. Groups that share a zone still interact: when a group replaces a lost member with an engine that a later group held, the later group picks a replacement too.
  • Level 2, gateways and engines. Within a group, segments are assigned in id order. Each goes to the member with the highest hash whose load stays within 1.25× the group’s average bytes, otherwise to the least loaded member. Each tail fragment is placed the same way, as a segment with an equal share of the tail’s bytes. Level 2 uses integer arithmetic only, so the Go router and the Rust engines compute the same mapping; shared test vectors pin the two together.
  • Make-before-break. The controller keeps a serving and a target placement for each index. Engines warm the target. Serving switches to the target once every new owner reports warm, or after warmup_timeout.

Engines (weed-vector-worker)

Flag Env Default Meaning
--masters VECTOR_MASTERS — Comma-separated master addresses. gRPC is the HTTP port + 10000 unless given as host:port.grpcPort.
--listen VECTOR_LISTEN 0.0.0.0:18888 gRPC listen address.
--state-dir VECTOR_STATE_DIR <tmp>/weed-vector-<port> Keeps the engine’s id, so a restart within restart_grace gets its slots back. Use a directory that survives reboots.
--zone VECTOR_ZONE default Failure domain (data center, or data center/rack). An index’s groups go to different zones.
--capacity-bytes VECTOR_CAPACITY_BYTES 75% of RAM, or of the cgroup limit Segment memory budget, and the engine’s placement weight.
--storage-option VECTOR_STORAGE_OPTIONS — key=value object store option for opening assigned tables (endpoint, region, credentials). Repeatable; comma-separated in the env var.
--table-root VECTOR_TABLE_ROOT unset URI prefix every table must live under. Requests for other URIs are denied. Unset, the engine opens any URI a caller names, so keep engine ports on a private network.
--max-warmups VECTOR_MAX_WARMUPS 4 Slots warmed at the same time.
--drain-timeout VECTOR_DRAIN_TIMEOUT 2m How long SIGTERM waits for the controller to move queries off this engine before the server stops.
--search-threads VECTOR_SEARCH_THREADS one per CPU core Searches running at once.
--search-queue VECTOR_SEARCH_QUEUE 2 × search threads Searches waiting for a thread. Beyond that, new searches get RESOURCE_EXHAUSTED at once.
--dataset-cache VECTOR_DATASET_CACHE 256 Opened (table, version) datasets kept in memory.
--index-cache-bytes VECTOR_INDEX_CACHE_BYTES the capacity, else 6 GiB Byte budget for cached index data, shared by every table.
--read-cache-mb VECTOR_READ_CACHE_MB 512 Memory for caching reads of write-once table files. 0 disables it.

Engine states: joining (registered, not serving), warming (loading its target slots), serving, draining (planned removal: still serving until queries move off). An engine whose session stays lost past restart_grace is gone, and its slots are placed elsewhere.

Query router (every S3 gateway)

The router needs no flags. It finds the controller through the gateway’s masters and uses the gateway’s -dataCenter as its zone.

  1. Resolve the index’s latest version. The version is cached, and writes through the gateway invalidate it.
  2. List the version’s segments from any serving engine. The list is cached per version, because versions are immutable.
  3. Watch the index’s placement, and pick a group: the gateway’s own zone first, then the group with fewer outstanding requests.
  4. Map segments and tail fragments to the group’s members. A member that is down or shedding load hands its share to its owner in another group (warm), or else to another member (cold).
  5. Send one SearchSegments per engine, with the query’s remaining deadline.
  6. Merge the partial top-k lists into the global top-k.

With no placement (no controller, or no serving engine), the query goes to the single weed-vector. Only QueryVectors takes the distributed path. Writes and index management go to the single engine.

Policy Default
Outstanding requests per engine 32
Hedge After the engine’s p95 latency, to the owner in another group, once 20 of that engine’s latencies have been seen. Budget: 5% of sub-requests.
Retry Once, on another owner, after RESOURCE_EXHAUSTED or a broken connection. Budget: 10% of sub-requests plus 1 per second.
Engine that failed or shed load Avoided for 1 s, doubling with each consecutive failure up to 32 s.
Queries in flight per gateway Adaptive between 16 and 1024, starting at 64. A query waits up to 20 ms for room.
Widest plan 64 engines. Wider plans are refused.

Errors

Cause S3 Vectors error
Engines are shedding load or unreachable, after the one retry ServiceUnavailableException (503)
The gateway’s in-flight limit is still reached after the 20 ms wait ServiceUnavailableException (503)
The plan would span more than 64 engines ServiceUnavailableException (503)

An engine that sheds load means the cluster is short of capacity, not that the caller is sending too fast, so the gateway never reports it as TooManyRequestsException.

Versions

Index versions are immutable. Engines keep opened versions in an LRU. There is no per-query lease: version cleanup keeps every version that was the latest at any point in the last hour, however old its commit, and no query runs that long.