1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//! Async wrappers for blocking storage operations.
//!
//! EPIC-034/US-001: Provides `spawn_blocking` wrappers for I/O-intensive
//! storage operations to avoid blocking the async executor.
//!
//! # Why spawn_blocking?
//!
//! Memory-mapped file operations (mmap resize, flush, compaction) perform
//! blocking syscalls that can stall the async runtime. This module wraps
//! these operations to run on Tokio's blocking thread pool.
//!
//! # Usage
//!
//! ```rust,ignore
//! use velesdb_core::storage::{MmapStorage, async_ops};
//!
//! async fn bulk_import(storage: Arc<RwLock<MmapStorage>>) {
//! // Pre-allocate in blocking thread
//! async_ops::reserve_capacity_async(storage.clone(), 1_000_000).await?;
//!
//! // Then insert vectors...
//! }
//! ```
use RwLock;
use io;
use Arc;
use VectorStorage;
use MmapStorage;
/// Asynchronously reserves storage capacity for a known number of vectors.
///
/// Wraps `MmapStorage::reserve_capacity()` in `spawn_blocking` to avoid
/// blocking the async executor during file resize operations.
///
/// # Arguments
///
/// * `storage` - Arc-wrapped storage instance
/// * `vector_count` - Expected number of vectors to store
///
/// # Errors
///
/// Returns an error if file operations fail or if the blocking task panics.
pub async
/// Asynchronously compacts the storage by rewriting only active vectors.
///
/// Wraps `MmapStorage::compact()` in `spawn_blocking` to avoid blocking
/// the async executor during the potentially long compaction operation.
///
/// # Returns
///
/// The number of bytes reclaimed.
///
/// # Errors
///
/// Returns an error if file operations fail or if the blocking task panics.
pub async
/// Asynchronously flushes the storage to disk.
///
/// Wraps `MmapStorage::flush()` in `spawn_blocking` to avoid blocking
/// the async executor during disk sync operations.
///
/// # Errors
///
/// Returns an error if file operations fail or if the blocking task panics.
pub async
/// Asynchronously stores a batch of vectors, paying one durability barrier
/// for the whole batch.
///
/// Wraps bulk insertion in `spawn_blocking` for large batches that would
/// otherwise block the async executor.
///
/// This used to loop over [`VectorStorage::store`], which under
/// [`DurabilityMode::Fsync`](super::DurabilityMode::Fsync) issues its own
/// `flush` + `sync_all` per vector — so a 10 000-vector batch paid 10 000
/// fsyncs, every one of them while holding the storage write lock, which is
/// the opposite of what the name promises. [`VectorStorage::store_batch`]
/// coalesces the WAL entries into a single grouped write and deliberately
/// leaves the barrier to the caller; the explicit `flush` afterwards is that
/// barrier.
///
/// The durability guarantee on return is unchanged — every entry is on disk
/// when this resolves under `Fsync` — because `flush` dispatches on the same
/// mode the per-vector path consulted. It is in fact marginally stronger: the
/// mmap is flushed too, which the per-vector loop never did.
///
/// # Arguments
///
/// * `storage` - Arc-wrapped storage instance
/// * `vectors` - Vector of (id, vector_data) pairs
///
/// # Errors
///
/// Returns an error if any dimension is wrong, if the write fails, or if the
/// durability barrier fails. Dimensions are validated for the whole batch
/// before anything is written, so a malformed entry rejects the batch instead
/// of leaving the prefix before it committed.
pub async