1use crate::KevyResult;
15use std::sync::RwLockWriteGuard;
16
17use crate::shard::shard_idx;
18use crate::store::{Inner, Store, commit_write, store_err};
19
20use crate::store::ensure_writable;
21
22type ShardUndoEntry = (usize, Vec<u8>, Option<(kevy_store::Value, Option<u64>)>);
25
26#[derive(Debug)]
42pub struct AtomicAllShards<'a> {
43 pub(crate) guards: Vec<RwLockWriteGuard<'a, Inner>>,
44 log: Vec<(usize, Vec<Vec<u8>>)>,
46 undo: Vec<ShardUndoEntry>,
52 touched: std::collections::HashSet<Vec<u8>>,
53 #[cfg(feature = "index")]
58 pub(crate) indexes: std::sync::Arc<crate::ops_index::IndexReg>,
59}
60
61impl<'a> AtomicAllShards<'a> {
62 pub(crate) fn idx(&self, key: &[u8]) -> usize {
63 shard_idx(key, self.guards.len())
64 }
65
66 fn snap(&mut self, key: &[u8]) {
68 if self.touched.contains(key) {
69 return;
70 }
71 let i = self.idx(key);
72 let prior = self.guards[i].store.clone_with_ttl(key);
73 self.touched.insert(key.to_vec());
74 self.undo.push((i, key.to_vec(), prior));
75 }
76
77 fn log_arg(&mut self, idx: usize, parts: &[&[u8]]) {
78 self.log.push((idx, parts.iter().map(|p| p.to_vec()).collect()));
79 }
80
81 pub fn set(&mut self, key: &[u8], value: &[u8]) -> bool {
85 self.snap(key);
86 let i = self.idx(key);
87 let ok =
88 self.guards[i].store.set(key, value.to_vec(), None, kevy_store::SetCondition::Always);
89 self.log_arg(i, &[b"SET", key, value]);
90 ok
91 }
92
93 pub fn get(&mut self, key: &[u8]) -> KevyResult<Option<Vec<u8>>> {
95 let i = self.idx(key);
96 self.guards[i]
97 .store
98 .get(key)
99 .map(|opt| opt.as_deref().map(<[u8]>::to_vec))
100 .map_err(store_err)
101 }
102
103 pub fn incr(&mut self, key: &[u8]) -> KevyResult<i64> {
105 self.snap(key);
106 let i = self.idx(key);
107 let n = self.guards[i].store.incr_by(key, 1).map_err(store_err)?;
108 self.log_arg(i, &[b"INCR", key]);
109 Ok(n)
110 }
111
112 pub fn incr_by(&mut self, key: &[u8], delta: i64) -> KevyResult<i64> {
114 self.snap(key);
115 let i = self.idx(key);
116 let n = self.guards[i].store.incr_by(key, delta).map_err(store_err)?;
117 let s = format!("{delta}");
118 self.log_arg(i, &[b"INCRBY", key, s.as_bytes()]);
119 Ok(n)
120 }
121
122 pub fn hset(&mut self, key: &[u8], pairs: &[(&[u8], &[u8])]) -> KevyResult<usize> {
127 self.snap(key);
128 let i = self.idx(key);
129 let n = self.guards[i].store.hset(key, pairs).map_err(store_err)?;
130 let mut parts: Vec<&[u8]> = Vec::with_capacity(2 + pairs.len() * 2);
131 parts.push(b"HSET");
132 parts.push(key);
133 for (f, v) in pairs {
134 parts.push(f);
135 parts.push(v);
136 }
137 self.log_arg(i, &parts);
138 Ok(n)
139 }
140
141 pub fn hget(&mut self, key: &[u8], field: &[u8]) -> KevyResult<Option<Vec<u8>>> {
143 let i = self.idx(key);
144 Ok(self.guards[i].store.hget(key, field).map_err(store_err)?.map(<[u8]>::to_vec))
145 }
146
147 pub fn hincrby(&mut self, key: &[u8], field: &[u8], delta: i64) -> KevyResult<i64> {
149 self.snap(key);
150 let i = self.idx(key);
151 let n = self.guards[i].store.hincrby(key, field, delta).map_err(store_err)?;
152 let s = format!("{delta}");
153 self.log_arg(i, &[b"HINCRBY", key, field, s.as_bytes()]);
154 Ok(n)
155 }
156
157 pub fn zadd(&mut self, key: &[u8], pairs: &[(f64, &[u8])]) -> KevyResult<usize> {
162 self.snap(key);
163 let i = self.idx(key);
164 let n = self.guards[i].store.zadd(key, pairs).map_err(store_err)?;
165 let score_strs: Vec<Vec<u8>> =
166 pairs.iter().map(|(s, _)| format!("{s}").into_bytes()).collect();
167 let mut parts: Vec<&[u8]> = Vec::with_capacity(2 + pairs.len() * 2);
168 parts.push(b"ZADD");
169 parts.push(key);
170 for (j, (_, m)) in pairs.iter().enumerate() {
171 parts.push(&score_strs[j]);
172 parts.push(m);
173 }
174 self.log_arg(i, &parts);
175 Ok(n)
176 }
177
178 pub fn zincrby(&mut self, key: &[u8], delta: f64, member: &[u8]) -> KevyResult<f64> {
180 self.snap(key);
181 let i = self.idx(key);
182 let n = self.guards[i].store.zincrby(key, delta, member).map_err(store_err)?;
183 let s = format!("{delta}");
184 self.log_arg(i, &[b"ZINCRBY", key, s.as_bytes(), member]);
185 Ok(n)
186 }
187
188 pub fn zscore(&mut self, key: &[u8], member: &[u8]) -> KevyResult<Option<f64>> {
190 let i = self.idx(key);
191 self.guards[i].store.zscore(key, member).map_err(store_err)
192 }
193
194 pub fn del(&mut self, keys: &[&[u8]]) -> usize {
199 for k in keys {
200 self.snap(k);
201 }
202 let mut n = 0;
203 for k in keys {
204 let i = self.idx(k);
205 if self.guards[i].store.del(&[k]) > 0 {
206 n += 1;
207 self.log_arg(i, &[b"DEL", k]);
208 }
209 }
210 n
211 }
212
213 pub fn exists(&mut self, keys: &[&[u8]]) -> usize {
215 keys.iter()
216 .filter(|k| {
217 let i = self.idx(k);
218 self.guards[i].store.key_exists(k)
219 })
220 .count()
221 }
222
223 pub fn hdel(&mut self, key: &[u8], fields: &[&[u8]]) -> KevyResult<usize> {
227 self.snap(key);
228 let i = self.idx(key);
229 let removed = self.guards[i].store.hdel(key, fields).map_err(store_err)?;
230 if removed > 0 {
231 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + fields.len());
232 argv.push(b"HDEL");
233 argv.push(key);
234 argv.extend_from_slice(fields);
235 self.log_arg(i, &argv);
236 }
237 Ok(removed)
238 }
239
240 pub fn hgetall(&mut self, key: &[u8]) -> KevyResult<Vec<(Vec<u8>, Vec<u8>)>> {
242 let i = self.idx(key);
243 let flat = self.guards[i].store.hgetall(key).map_err(store_err)?;
244 let mut out = Vec::with_capacity(flat.len() / 2);
245 let mut it = flat.into_iter();
246 while let (Some(f), Some(v)) = (it.next(), it.next()) {
247 out.push((f, v));
248 }
249 Ok(out)
250 }
251
252 pub fn hmget(&mut self, key: &[u8], fields: &[&[u8]]) -> KevyResult<Vec<Option<Vec<u8>>>> {
254 let i = self.idx(key);
255 self.guards[i].store.hmget(key, fields).map_err(store_err)
256 }
257
258 pub fn hexists(&mut self, key: &[u8], field: &[u8]) -> KevyResult<bool> {
260 let i = self.idx(key);
261 self.guards[i].store.hexists(key, field).map_err(store_err)
262 }
263
264 pub fn sadd(&mut self, key: &[u8], members: &[&[u8]]) -> KevyResult<usize> {
268 self.snap(key);
269 let i = self.idx(key);
270 let added = self.guards[i].store.sadd(key, members).map_err(store_err)?;
271 if added > 0 {
272 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + members.len());
273 argv.push(b"SADD");
274 argv.push(key);
275 argv.extend_from_slice(members);
276 self.log_arg(i, &argv);
277 }
278 Ok(added)
279 }
280
281 pub fn srem(&mut self, key: &[u8], members: &[&[u8]]) -> KevyResult<usize> {
283 self.snap(key);
284 let i = self.idx(key);
285 let removed = self.guards[i].store.srem(key, members).map_err(store_err)?;
286 if removed > 0 {
287 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + members.len());
288 argv.push(b"SREM");
289 argv.push(key);
290 argv.extend_from_slice(members);
291 self.log_arg(i, &argv);
292 }
293 Ok(removed)
294 }
295
296 pub fn lpush(&mut self, key: &[u8], values: &[&[u8]]) -> KevyResult<usize> {
300 self.snap(key);
301 let i = self.idx(key);
302 let len = self.guards[i].store.lpush(key, values).map_err(store_err)?;
303 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + values.len());
304 argv.push(b"LPUSH");
305 argv.push(key);
306 argv.extend_from_slice(values);
307 self.log_arg(i, &argv);
308 Ok(len)
309 }
310
311 pub fn rpush(&mut self, key: &[u8], values: &[&[u8]]) -> KevyResult<usize> {
313 self.snap(key);
314 let i = self.idx(key);
315 let len = self.guards[i].store.rpush(key, values).map_err(store_err)?;
316 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + values.len());
317 argv.push(b"RPUSH");
318 argv.push(key);
319 argv.extend_from_slice(values);
320 self.log_arg(i, &argv);
321 Ok(len)
322 }
323
324 pub fn zrem(&mut self, key: &[u8], members: &[&[u8]]) -> KevyResult<usize> {
328 self.snap(key);
329 let i = self.idx(key);
330 let removed = self.guards[i].store.zrem(key, members).map_err(store_err)?;
331 if removed > 0 {
332 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + members.len());
333 argv.push(b"ZREM");
334 argv.push(key);
335 argv.extend_from_slice(members);
336 self.log_arg(i, &argv);
337 }
338 Ok(removed)
339 }
340
341 pub fn zcard(&mut self, key: &[u8]) -> KevyResult<usize> {
343 let i = self.idx(key);
344 self.guards[i].store.zcard(key).map_err(store_err)
345 }
346
347 pub fn zadd_flags(
350 &mut self,
351 key: &[u8],
352 pairs: &[(f64, &[u8])],
353 flags: kevy_store::ZaddFlags,
354 ) -> KevyResult<kevy_store::ZaddReport> {
355 let i = self.idx(key);
356 let rep = self.guards[i].store.zadd_flags(key, pairs, flags).map_err(store_err)?;
357 if !rep.applied.is_empty() {
358 let score_strs: Vec<Vec<u8>> =
359 rep.applied.iter().map(|(s, _)| format!("{s}").into_bytes()).collect();
360 let mut parts: Vec<&[u8]> = Vec::with_capacity(2 + rep.applied.len() * 2);
361 parts.push(b"ZADD");
362 parts.push(key);
363 for (j, (_, m)) in rep.applied.iter().enumerate() {
364 parts.push(&score_strs[j]);
365 parts.push(m);
366 }
367 self.log_arg(i, &parts);
368 }
369 Ok(rep)
370 }
371}
372
373impl Store {
374 pub fn atomic_all_shards<R>(
383 &self,
384 body: impl FnOnce(&mut AtomicAllShards<'_>) -> KevyResult<R>,
385 ) -> KevyResult<R> {
386 ensure_writable(self)?;
387 let guards: Vec<RwLockWriteGuard<'_, Inner>> =
390 self.shards.iter().map(|s| s.write().expect("lock poisoned")).collect();
391 let mut ctx = AtomicAllShards {
392 guards,
393 log: Vec::new(),
394 undo: Vec::new(),
395 touched: std::collections::HashSet::new(),
396 #[cfg(feature = "index")]
397 indexes: std::sync::Arc::clone(&self.indexes),
398 };
399 let outcome = body(&mut ctx);
400 let log = std::mem::take(&mut ctx.log);
401 let undo = std::mem::take(&mut ctx.undo);
402 let r = match outcome {
403 Ok(r) => r,
404 Err(e) => {
405 rollback_all(&mut ctx.guards, undo);
406 return Err(e);
407 }
408 };
409 commit_group_all(&mut ctx.guards, log)?;
410 Ok(r)
411 }
412}
413
414#[cfg_attr(not(test), allow(dead_code))]
418pub(crate) const ATOMIC_ALL_OPS: &[&str] = &[
419 "SET",
420 "GET",
421 "INCR",
422 "INCRBY",
423 "HSET",
424 "HGET",
425 "HINCRBY",
426 "ZADD",
427 "ZINCRBY",
428 "ZSCORE",
429 "DEL",
430 "EXISTS",
431 "HDEL",
432 "HGETALL",
433 "HMGET",
434 "HEXISTS",
435 "SADD",
436 "SREM",
437 "LPUSH",
438 "RPUSH",
439 "ZREM",
440 "ZCARD",
441 "SMEMBERS",
442 "SISMEMBER",
443 "LRANGE",
444 "LLEN",
445 "SCARD",
446 "ZRANGEBYSCORE",
447];
448
449fn rollback_all(guards: &mut [RwLockWriteGuard<'_, Inner>], undo: Vec<ShardUndoEntry>) {
452 for (idx, key, prior) in undo.into_iter().rev() {
453 let g = &mut guards[idx];
454 match prior {
455 Some((value, ttl_ms)) => g.store.put_with_ttl(key, value, ttl_ms),
456 None => {
457 let k: &[u8] = &key;
458 g.store.del(&[k]);
459 }
460 }
461 }
462}
463
464fn commit_group_all(
468 guards: &mut [RwLockWriteGuard<'_, Inner>],
469 log: Vec<(usize, Vec<Vec<u8>>)>,
470) -> KevyResult<()> {
471 #[cfg(feature = "persist")]
472 for g in guards.iter_mut() {
473 if let Some(aof) = g.aof.as_mut() {
474 aof.begin_group();
475 }
476 }
477 let mut commit = Ok(());
478 for (idx, parts) in log {
479 let g = &mut guards[idx];
480 let refs: Vec<&[u8]> = parts.iter().map(|v| v.as_slice()).collect();
481 commit = commit_write(g, &refs);
482 if commit.is_err() {
483 break;
484 }
485 }
486 #[cfg(feature = "persist")]
487 for g in guards.iter_mut() {
488 if let Some(aof) = g.aof.as_mut() {
489 let synced = aof.end_group().map_err(crate::KevyError::from);
490 if commit.is_ok() {
491 commit = synced;
492 }
493 }
494 }
495 commit
496}