Write-optimized ingest, incremental indexing, and freshness-aware query routing
At thousands of writes per second, the write path becomes the bottleneck, and the architectural changes are about decoupling ingestion from indexing, keeping the write path cheap, and making new data searchable quickly without sacrificing query latency. The first change is to batch writes: instead of upserting points one at a time, buffer them in the application and upsert in batches of hundreds or thousands, with wait=False to avoid blocking on durability. Batching amortizes the WAL write and the segment insert cost. The second change is to tune the optimizer for high write throughput: raise indexing_threshold during the high-write period so the optimizer does not try to build and merge segments on every batch, and lower it during quiet periods to consolidate. The third change is to use a write-optimized segment sizing: many small segments are fast to write but expensive to query, so the optimizer thresholds must balance write throughput against query fan-out. The fourth change is to consider a two-tier architecture: a small, fast, in-memory hot tier for the most recent data, and a larger, on-disk cold tier for older data. The fifth change is to make the ingest pipeline resilient: use a durable queue between the source and Qdrant, and make upserts idempotent with deterministic IDs so retries are safe.
The mechanism that makes near-real-time search possible is that Qdrant's segment model accepts new writes into a small mutable segment that is immediately searchable, even before it is indexed. So a point that is upserted is visible to queries within milliseconds, even if the HNSW graph for its segment has not been built yet. The query falls back to a brute-force scan on the unindexed segment, which is fast for a small segment. The optimizer then indexes the segment in the background, which makes future queries faster. This is the core of Qdrant's near-real-time behavior: writes are always visible, and indexing happens asynchronously. The freshness/latency trade-off is that unindexed segments make queries slower, so there is a tension between keeping the write path fast and keeping queries fast. The right balance depends on the ratio of writes to queries and on how fresh the data needs to be. The other mechanism is the WAL, which provides durability and replication: at high write rates, the WAL becomes the throughput limiter, and its size and flush behavior must be tuned to avoid I/O contention.
Batch writes: accumulate points and upsert in batches with wait=False.
Tune the optimizer: raise indexing_threshold during high-write periods, lower it during quiet periods.
Balance segment size: small segments are fast to write but expensive to query; tune the optimizer.
Hot/cold tiering: a small in-memory hot tier for recent data and an on-disk cold tier for history.
Durable queue: a queue between the source and Qdrant absorbs bursts and provides backpressure.
Idempotent upserts: deterministic point IDs make retries safe under at-least-once delivery.
WAL tuning: wal_capacity_mb and related settings to handle the write rate.
Replication: replication_factor > 1 so a node failure does not lose recent writes.
Freshness SLO: decide how stale the search index can be and tune the optimizer accordingly.
The trade-off is between write throughput, query latency, and freshness. Aggressive indexing keeps queries fast but competes with writes for CPU and I/O, reducing write throughput. Deferred indexing keeps writes fast but leaves data unindexed, which makes queries slower and can cause latency spikes. The right balance depends on the workload. The common mistakes are: (1) upserting one point at a time, which serializes the write path and wastes throughput; (2) leaving indexing_threshold at its default during a high-write period, so the optimizer churns and slows both reads and writes; (3) not using a durable queue, so a Qdrant outage causes data loss; (4) not making upserts idempotent, so retries create duplicates; (5) not accounting for the WAL's disk usage at high write rates, which can fill the disk. Version note: the optimizer thresholds, the WAL configuration, and the segment model have evolved across Qdrant releases. In particular, the way indexing_threshold is interpreted and the availability of incremental indexing optimizations have changed. Verify the behavior on your version and benchmark the write path at your expected rate.
Version-dependent: the optimizer thresholds, WAL configuration, and incremental indexing behavior have changed across Qdrant releases. In some versions, the optimizer can be more aggressive about indexing new data without a rebuild; in others, the segment model dominates. Verify the behavior on your version with your write rate and query pattern.
You upsert one point at a time and the write rate is too low. Explain why batching helps and how to configure it.
A teammate says indexing must be disabled to handle the write rate. Explain the trade-off and the better approach.
Your live-feed queries are slow because most data is unindexed. Describe how you would balance indexing against write throughput.
You need to handle a burst of 50k writes per second for 10 minutes. Describe the architecture that absorbs the burst without losing data.
Design a live-feed search system that must accept 10k writes per second continuously with a 20ms p99 for queries over the last hour and a 100ms p99 for all history.
You need to support exactly-once ingestion from a source that can deliver duplicates. Describe the idempotency and deduplication strategy.
Derive the maximum sustainable write rate for a shard as a function of segment size, indexing throughput, and WAL bandwidth. Where does the model predict the system falls behind?
You are designing a system that must ingest a global firehose of events and serve interactive search over the last hour. Describe the architecture and the trade-offs.