1use crate::{KevyError, 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)]
29pub struct AtomicAllShards<'a> {
30 pub(crate) guards: Vec<RwLockWriteGuard<'a, Inner>>,
31 log: Vec<(usize, Vec<Vec<u8>>)>,
33 undo: Vec<ShardUndoEntry>,
39 touched: std::collections::HashSet<Vec<u8>>,
40 #[cfg(feature = "index")]
45 pub(crate) indexes: std::sync::Arc<crate::ops_index::IndexReg>,
46}
47
48impl<'a> AtomicAllShards<'a> {
49 pub(crate) fn idx(&self, key: &[u8]) -> usize {
50 shard_idx(key, self.guards.len())
51 }
52
53 fn snap(&mut self, key: &[u8]) {
55 if self.touched.contains(key) {
56 return;
57 }
58 let i = self.idx(key);
59 let prior = self.guards[i].store.clone_with_ttl(key);
60 self.touched.insert(key.to_vec());
61 self.undo.push((i, key.to_vec(), prior));
62 }
63
64 fn log_arg(&mut self, idx: usize, parts: &[&[u8]]) {
65 self.log.push((idx, parts.iter().map(|p| p.to_vec()).collect()));
66 }
67
68 pub fn set(&mut self, key: &[u8], value: &[u8]) -> bool {
72 self.snap(key);
73 let i = self.idx(key);
74 let ok = self.guards[i].store.set(key, value.to_vec(), None, false, false);
75 self.log_arg(i, &[b"SET", key, value]);
76 ok
77 }
78
79 pub fn get(&mut self, key: &[u8]) -> KevyResult<Option<Vec<u8>>> {
81 let i = self.idx(key);
82 self.guards[i]
83 .store
84 .get(key)
85 .map(|opt| opt.as_deref().map(<[u8]>::to_vec))
86 .map_err(store_err)
87 }
88
89 pub fn incr(&mut self, key: &[u8]) -> KevyResult<i64> {
91 self.snap(key);
92 let i = self.idx(key);
93 let n = self.guards[i].store.incr_by(key, 1).map_err(store_err)?;
94 self.log_arg(i, &[b"INCR", key]);
95 Ok(n)
96 }
97
98 pub fn incr_by(&mut self, key: &[u8], delta: i64) -> KevyResult<i64> {
100 self.snap(key);
101 let i = self.idx(key);
102 let n = self.guards[i].store.incr_by(key, delta).map_err(store_err)?;
103 let s = format!("{delta}");
104 self.log_arg(i, &[b"INCRBY", key, s.as_bytes()]);
105 Ok(n)
106 }
107
108 pub fn hset(&mut self, key: &[u8], pairs: &[(&[u8], &[u8])]) -> KevyResult<usize> {
113 self.snap(key);
114 let i = self.idx(key);
115 let n = self.guards[i].store.hset(key, pairs).map_err(store_err)?;
116 let mut parts: Vec<&[u8]> = Vec::with_capacity(2 + pairs.len() * 2);
117 parts.push(b"HSET");
118 parts.push(key);
119 for (f, v) in pairs {
120 parts.push(f);
121 parts.push(v);
122 }
123 self.log_arg(i, &parts);
124 Ok(n)
125 }
126
127 pub fn hget(&mut self, key: &[u8], field: &[u8]) -> KevyResult<Option<Vec<u8>>> {
129 let i = self.idx(key);
130 Ok(self.guards[i].store.hget(key, field).map_err(store_err)?.map(<[u8]>::to_vec))
131 }
132
133 pub fn hincrby(&mut self, key: &[u8], field: &[u8], delta: i64) -> KevyResult<i64> {
135 self.snap(key);
136 let i = self.idx(key);
137 let n = self.guards[i].store.hincrby(key, field, delta).map_err(store_err)?;
138 let s = format!("{delta}");
139 self.log_arg(i, &[b"HINCRBY", key, field, s.as_bytes()]);
140 Ok(n)
141 }
142
143 pub fn zadd(&mut self, key: &[u8], pairs: &[(f64, &[u8])]) -> KevyResult<usize> {
148 self.snap(key);
149 let i = self.idx(key);
150 let n = self.guards[i].store.zadd(key, pairs).map_err(store_err)?;
151 let score_strs: Vec<Vec<u8>> =
152 pairs.iter().map(|(s, _)| format!("{s}").into_bytes()).collect();
153 let mut parts: Vec<&[u8]> = Vec::with_capacity(2 + pairs.len() * 2);
154 parts.push(b"ZADD");
155 parts.push(key);
156 for (j, (_, m)) in pairs.iter().enumerate() {
157 parts.push(&score_strs[j]);
158 parts.push(m);
159 }
160 self.log_arg(i, &parts);
161 Ok(n)
162 }
163
164 pub fn zincrby(&mut self, key: &[u8], delta: f64, member: &[u8]) -> KevyResult<f64> {
166 self.snap(key);
167 let i = self.idx(key);
168 let n = self.guards[i].store.zincrby(key, delta, member).map_err(store_err)?;
169 let s = format!("{delta}");
170 self.log_arg(i, &[b"ZINCRBY", key, s.as_bytes(), member]);
171 Ok(n)
172 }
173
174 pub fn zscore(&mut self, key: &[u8], member: &[u8]) -> KevyResult<Option<f64>> {
176 let i = self.idx(key);
177 self.guards[i].store.zscore(key, member).map_err(store_err)
178 }
179
180 pub fn del(&mut self, keys: &[&[u8]]) -> usize {
185 for k in keys {
186 self.snap(k);
187 }
188 let mut n = 0;
189 for k in keys {
190 let i = self.idx(k);
191 if self.guards[i].store.del(&[k]) > 0 {
192 n += 1;
193 self.log_arg(i, &[b"DEL", k]);
194 }
195 }
196 n
197 }
198
199 pub fn exists(&mut self, keys: &[&[u8]]) -> usize {
201 keys.iter()
202 .filter(|k| {
203 let i = self.idx(k);
204 self.guards[i].store.key_exists(k)
205 })
206 .count()
207 }
208
209 pub fn hdel(&mut self, key: &[u8], fields: &[&[u8]]) -> KevyResult<usize> {
213 self.snap(key);
214 let i = self.idx(key);
215 let removed = self.guards[i].store.hdel(key, fields).map_err(store_err)?;
216 if removed > 0 {
217 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + fields.len());
218 argv.push(b"HDEL");
219 argv.push(key);
220 argv.extend_from_slice(fields);
221 self.log_arg(i, &argv);
222 }
223 Ok(removed)
224 }
225
226 pub fn hgetall(&mut self, key: &[u8]) -> KevyResult<Vec<(Vec<u8>, Vec<u8>)>> {
228 let i = self.idx(key);
229 let flat = self.guards[i].store.hgetall(key).map_err(store_err)?;
230 let mut out = Vec::with_capacity(flat.len() / 2);
231 let mut it = flat.into_iter();
232 while let (Some(f), Some(v)) = (it.next(), it.next()) {
233 out.push((f, v));
234 }
235 Ok(out)
236 }
237
238 pub fn hmget(&mut self, key: &[u8], fields: &[&[u8]]) -> KevyResult<Vec<Option<Vec<u8>>>> {
240 let i = self.idx(key);
241 self.guards[i].store.hmget(key, fields).map_err(store_err)
242 }
243
244 pub fn hexists(&mut self, key: &[u8], field: &[u8]) -> KevyResult<bool> {
246 let i = self.idx(key);
247 self.guards[i].store.hexists(key, field).map_err(store_err)
248 }
249
250 pub fn sadd(&mut self, key: &[u8], members: &[&[u8]]) -> KevyResult<usize> {
254 self.snap(key);
255 let i = self.idx(key);
256 let added = self.guards[i].store.sadd(key, members).map_err(store_err)?;
257 if added > 0 {
258 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + members.len());
259 argv.push(b"SADD");
260 argv.push(key);
261 argv.extend_from_slice(members);
262 self.log_arg(i, &argv);
263 }
264 Ok(added)
265 }
266
267 pub fn srem(&mut self, key: &[u8], members: &[&[u8]]) -> KevyResult<usize> {
269 self.snap(key);
270 let i = self.idx(key);
271 let removed = self.guards[i].store.srem(key, members).map_err(store_err)?;
272 if removed > 0 {
273 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + members.len());
274 argv.push(b"SREM");
275 argv.push(key);
276 argv.extend_from_slice(members);
277 self.log_arg(i, &argv);
278 }
279 Ok(removed)
280 }
281
282 pub fn lpush(&mut self, key: &[u8], values: &[&[u8]]) -> KevyResult<usize> {
286 self.snap(key);
287 let i = self.idx(key);
288 let len = self.guards[i].store.lpush(key, values).map_err(store_err)?;
289 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + values.len());
290 argv.push(b"LPUSH");
291 argv.push(key);
292 argv.extend_from_slice(values);
293 self.log_arg(i, &argv);
294 Ok(len)
295 }
296
297 pub fn rpush(&mut self, key: &[u8], values: &[&[u8]]) -> KevyResult<usize> {
299 self.snap(key);
300 let i = self.idx(key);
301 let len = self.guards[i].store.rpush(key, values).map_err(store_err)?;
302 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + values.len());
303 argv.push(b"RPUSH");
304 argv.push(key);
305 argv.extend_from_slice(values);
306 self.log_arg(i, &argv);
307 Ok(len)
308 }
309
310 pub fn zrem(&mut self, key: &[u8], members: &[&[u8]]) -> KevyResult<usize> {
314 self.snap(key);
315 let i = self.idx(key);
316 let removed = self.guards[i].store.zrem(key, members).map_err(store_err)?;
317 if removed > 0 {
318 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + members.len());
319 argv.push(b"ZREM");
320 argv.push(key);
321 argv.extend_from_slice(members);
322 self.log_arg(i, &argv);
323 }
324 Ok(removed)
325 }
326
327 pub fn zcard(&mut self, key: &[u8]) -> KevyResult<usize> {
329 let i = self.idx(key);
330 self.guards[i].store.zcard(key).map_err(store_err)
331 }
332
333 pub fn zadd_flags(
336 &mut self,
337 key: &[u8],
338 pairs: &[(f64, &[u8])],
339 flags: kevy_store::ZaddFlags,
340 ) -> KevyResult<kevy_store::ZaddReport> {
341 if !flags.valid() {
342 return Err(KevyError::InvalidInput("invalid ZADD flag combo".into()));
343 }
344 let i = self.idx(key);
345 let rep = self.guards[i].store.zadd_flags(key, pairs, flags).map_err(store_err)?;
346 if !rep.applied.is_empty() {
347 let score_strs: Vec<Vec<u8>> =
348 rep.applied.iter().map(|(s, _)| format!("{s}").into_bytes()).collect();
349 let mut parts: Vec<&[u8]> = Vec::with_capacity(2 + rep.applied.len() * 2);
350 parts.push(b"ZADD");
351 parts.push(key);
352 for (j, (_, m)) in rep.applied.iter().enumerate() {
353 parts.push(&score_strs[j]);
354 parts.push(m);
355 }
356 self.log_arg(i, &parts);
357 }
358 Ok(rep)
359 }
360}
361
362impl Store {
363 pub fn atomic_all_shards<R>(
372 &self,
373 body: impl FnOnce(&mut AtomicAllShards<'_>) -> KevyResult<R>,
374 ) -> KevyResult<R> {
375 ensure_writable(self)?;
376 let guards: Vec<RwLockWriteGuard<'_, Inner>> =
379 self.shards.iter().map(|s| s.write().expect("lock poisoned")).collect();
380 let mut ctx = AtomicAllShards {
381 guards,
382 log: Vec::new(),
383 undo: Vec::new(),
384 touched: std::collections::HashSet::new(),
385 #[cfg(feature = "index")]
386 indexes: std::sync::Arc::clone(&self.indexes),
387 };
388 let outcome = body(&mut ctx);
389 let log = std::mem::take(&mut ctx.log);
390 let undo = std::mem::take(&mut ctx.undo);
391 let r = match outcome {
392 Ok(r) => r,
393 Err(e) => {
394 rollback_all(&mut ctx.guards, undo);
395 return Err(e);
396 }
397 };
398 commit_group_all(&mut ctx.guards, log)?;
399 Ok(r)
400 }
401}
402
403#[cfg_attr(not(test), allow(dead_code))]
407pub(crate) const ATOMIC_ALL_OPS: &[&str] = &[
408 "SET",
409 "GET",
410 "INCR",
411 "INCRBY",
412 "HSET",
413 "HGET",
414 "HINCRBY",
415 "ZADD",
416 "ZINCRBY",
417 "ZSCORE",
418 "DEL",
419 "EXISTS",
420 "HDEL",
421 "HGETALL",
422 "HMGET",
423 "HEXISTS",
424 "SADD",
425 "SREM",
426 "LPUSH",
427 "RPUSH",
428 "ZREM",
429 "ZCARD",
430 "SMEMBERS",
431 "SISMEMBER",
432 "LRANGE",
433 "LLEN",
434 "SCARD",
435 "ZRANGEBYSCORE",
436];
437
438fn rollback_all(guards: &mut [RwLockWriteGuard<'_, Inner>], undo: Vec<ShardUndoEntry>) {
441 for (idx, key, prior) in undo.into_iter().rev() {
442 let g = &mut guards[idx];
443 match prior {
444 Some((value, ttl_ms)) => g.store.put_with_ttl(key, value, ttl_ms),
445 None => {
446 let k: &[u8] = &key;
447 g.store.del(&[k]);
448 }
449 }
450 }
451}
452
453fn commit_group_all(
457 guards: &mut [RwLockWriteGuard<'_, Inner>],
458 log: Vec<(usize, Vec<Vec<u8>>)>,
459) -> KevyResult<()> {
460 #[cfg(feature = "persist")]
461 for g in guards.iter_mut() {
462 if let Some(aof) = g.aof.as_mut() {
463 aof.begin_group();
464 }
465 }
466 let mut commit = Ok(());
467 for (idx, parts) in log {
468 let g = &mut guards[idx];
469 let refs: Vec<&[u8]> = parts.iter().map(|v| v.as_slice()).collect();
470 commit = commit_write(g, &refs);
471 if commit.is_err() {
472 break;
473 }
474 }
475 #[cfg(feature = "persist")]
476 for g in guards.iter_mut() {
477 if let Some(aof) = g.aof.as_mut() {
478 let synced = aof.end_group().map_err(KevyError::from);
479 if commit.is_ok() {
480 commit = synced;
481 }
482 }
483 }
484 commit
485}