# How zvec Handles Concurrent Writes and Read/Write Isolation: A Deep Dive into the Storage Engine

> Discover how zvec manages concurrent writes and read/write isolation with shared mutexes for thread-safe operations and uninterrupted queries.

- Repository: [Alibaba/zvec](https://github.com/alibaba/zvec)
- Tags: deep-dive
- Published: 2026-02-16

---

**zvec isolates concurrent reads and writes using two `std::shared_mutex` instances—`schema_handle_mtx_` for shared schema access and `write_mtx_` for exclusive write serialization—ensuring thread-safe operations while allowing concurrent queries.**

The `alibaba/zvec` storage engine implements a sophisticated concurrency control mechanism to handle high-throughput vector database workloads. Understanding how zvec handles concurrent writes and read/write isolation is essential for developers building multi-threaded applications that require consistent data views without sacrificing query performance.

## The Dual-Mutex Architecture Behind zvec's Concurrency Control

At the core of zvec's thread safety are two distinct mutexes declared in [`src/include/zvec/db/collection.h`](https://github.com/alibaba/zvec/blob/main/src/include/zvec/db/collection.h). This separation of concerns allows fine-grained control over schema stability versus data mutation.

### schema_handle_mtx_: Guarding Schema Stability and Segment Metadata

The `schema_handle_mtx_` is a `std::shared_mutex` that protects the collection schema and the global view of segment metadata managed by the version manager. All public API calls—both reads and writes—acquire a **shared lock** (`std::shared_lock`) on this mutex before proceeding. This ensures that the schema cannot be altered while any operation is in progress, providing schema stability across the entire system.

### write_mtx_: Serializing Mutating Operations

The second mutex, `write_mtx_`, also a `std::shared_mutex`, is dedicated to serializing batches that modify the active writing segment. This includes **Insert**, **Update**, **Upsert**, **Delete**, and **Delete-by-filter** operations. Unlike the schema mutex, write operations acquire an **exclusive lock** (`std::lock_guard<std::shared_mutex>`) on `write_mtx_` inside `CollectionImpl::write_impl` and the delete-by-filter execution path. This guarantees that only one write batch can manipulate the writing segment at any given time.

## How zvec Handles Concurrent Writes: The Write Path Explained

The write path in `src/db/collection.cc` demonstrates a strict locking hierarchy that prevents write-write conflicts and ensures atomic segment transitions.

### Schema Validation Under Shared Lock

When a write operation begins, it first acquires a shared lock on `schema_handle_mtx_`. This step validates that the document structure conforms to the current schema and ensures that no concurrent schema alterations can occur during the validation phase.

### Exclusive Write Locking and Segment Management

Following schema validation, the writer acquires an exclusive lock on `write_mtx_`:

```cpp
std::lock_guard<std::shared_mutex> write_lock(write_mtx_);

```

This exclusive lock ensures that only one write batch runs at a time, eliminating write-write conflicts. While holding this lock, if the current writing segment reaches its maximum document count, `switch_to_new_segment_for_writing()` is invoked to create a new segment.

### Atomic Segment Switching and Version Updates

The segment switching process occurs while the exclusive write lock remains held. The new segment is created, the version manager is updated atomically, and the old segment is handed over to the `SegmentManager`. The `VersionManager` (defined in [`src/db/index/common/version_manager.h`](https://github.com/alibaba/zvec/blob/main/src/db/index/common/version_manager.h)) applies the new version metadata while the exclusive lock is still active, guaranteeing that readers see either the old version or the completely new version, never an intermediate state.

The source code contains a comment acknowledging potential future optimization: `// TODO: The granularity of the write_lock is too coarse.`

## Read/Write Isolation in zvec: The Read Path

All read-only APIs—including `Query`, `GroupByQuery`, `Fetch`, and `Stats`—implement optimistic concurrency control using only the shared schema mutex.

```cpp
Result<DocPtrList> CollectionImpl::Query(const VectorQuery &query) const {
    std::shared_lock lock(schema_handle_mtx_);   // shared read lock
    // ...
    auto segments = get_all_segments();          // includes persisted + writing segment
    return sql_engine_->execute(schema_, query, segments);
}

```

By acquiring only a shared lock on `schema_handle_mtx_`, read operations allow unlimited concurrent readers. Since readers never attempt to lock `write_mtx_`, they remain unblocked by ongoing write batches. This design achieves true read/write isolation: writers serialize through the exclusive write mutex while readers proceed concurrently through the shared schema mutex.

## Consistency Guarantees and Isolation Levels

zvec provides specific consistency guarantees through its locking hierarchy and version management:

*   **Schema Stability**: Every operation holds a shared lock on `schema_handle_mtx_`, preventing schema modifications during active reads or writes.
*   **Segment-Level Isolation**: Writes are applied exclusively to the single in-memory writing segment (managed through [`src/db/index/segment/segment.h`](https://github.com/alibaba/zvec/blob/main/src/db/index/segment/segment.h)), while reads operate on a snapshot of persisted segments retrieved from the `VersionManager`.
*   **Read-Committed Semantics**: Readers may see the current writing segment only after the writer has flushed or switched segments. This ensures readers never observe partially-written documents or intermediate states during segment transitions.
*   **Atomic Version Updates**: When a segment closes, the `VersionManager` applies the new version metadata while the exclusive `write_mtx_` lock is held, ensuring readers see consistent snapshots.

## Summary

*   zvec employs a **dual-mutex architecture** using `schema_handle_mtx_` and `write_mtx_` to handle concurrent writes and read/write isolation.
*   **Shared locks** on `schema_handle_mtx_` allow unlimited concurrent reads while protecting schema stability.
*   **Exclusive locks** on `write_mtx_` serialize write batches to the active writing segment, preventing write-write conflicts.
*   **Atomic segment switching** via the `VersionManager` ensures readers see consistent snapshots without intermediate states.
*   The design provides **read-committed isolation** with thread-safe concurrent query execution.

## Frequently Asked Questions

### What mutex does zvec use to protect the collection schema?

zvec uses `schema_handle_mtx_`, a `std::shared_mutex` declared in [`src/include/zvec/db/collection.h`](https://github.com/alibaba/zvec/blob/main/src/include/zvec/db/collection.h), to protect the collection schema and segment metadata. All public API operations acquire a shared lock on this mutex to ensure schema stability during execution.

### How does zvec prevent write-write conflicts?

zvec prevents write-write conflicts through `write_mtx_`, a `std::shared_mutex` that is exclusively locked during mutating operations. The `CollectionImpl::write_impl` method and delete-by-filter paths acquire an exclusive lock using `std::lock_guard<std::shared_mutex>`, ensuring only one write batch modifies the active writing segment at a time.

### Does zvec support concurrent reads during write operations?

Yes, zvec supports concurrent reads during write operations through its dual-mutex architecture. Read-only APIs such as `Query` and `Fetch` acquire only a shared lock on `schema_handle_mtx_`, which does not conflict with the exclusive `write_mtx_` held by writers. This allows unlimited concurrent readers to execute alongside active write batches without blocking.

### What isolation level does zvec provide for read operations?

zvec provides read-committed isolation semantics for read operations. Readers retrieve a snapshot of persisted segments from the `VersionManager` and may see the current writing segment only after the writer has flushed or switched segments. This ensures readers never observe partially-written documents or intermediate states during segment transitions.