What happens on an insert?
The new vector must find its neighbors and be linked into every layer it appears in. HNSW's insertion algorithm runs greedy searches through the graph plus mutations to existing nodes' neighbor lists. Each mutation is a synchronization point with concurrent queries. One insert is cheap; thousands per second are a standing tax on every traversal.
Why are deletes worse than inserts?
HNSW has no cheap delete. Removing a node properly means repairing every neighbor list that pointed at it, so engines mark it dead instead and filter tombstones at query time. Traversals now visit nodes that yield nothing, recall degrades as live paths thin out, and the eventual cleanup (rebuild or compaction) is a heavyweight job running alongside your traffic. The recall-degradation-under-updates problem for graph indexes is documented in FreshDiskANN (Singh et al., 2021), which exists precisely because naive streaming updates break graph ANN quality.
How do systems cope?
The common patterns all reduce contention on the serving structure: segment the index and merge in the background (LSM-style), route writes to a separate ingest path that builds fresh segments off the serving path, or decouple indexing from serving entirely so queries never share locks with ingestion. Look for one property: reads served from immutable structures, writes absorbed elsewhere, and merges running asynchronously.
This is how TopK's write path actually works: a log-writer service appends writes to a WAL backed by object storage, a separate compactor service consumes the log and builds read-optimized indexed files (LSM-style, with compaction planned and executed in parallel), and query executors only ever read immutable segments. Read and write paths run on separate node pools, so ingestion never takes a lock a query can feel.
Writes
upserts, deletes
WAL
durable on object storage
Compactor
builds indexed segments
Immutable segments
Query executors
reads never blocked
TopK's stated motivation is the failure mode this page describes: in coupled architectures, "write traffic can negatively affect queries, which usually leads to over-provisioning of resources to maintain SLAs".
What should you do?
If your workload has real write volume, benchmark under mixed read/write load, never read-only (how to benchmark), and prefer engines whose ingest path is decoupled from the query path.
The measured effect of this design in TopK: dynamic write batching sustains about 70MB/s of ingest, over 30,000 vectors per second, without sharing resources with the read path; concurrent-load measurements are on the benchmarks page.