1use crate::{KevyError, KevyResult};
14use std::sync::RwLockWriteGuard;
15
16use crate::store::ensure_writable;
17use crate::store::{Inner, Store, commit_write, store_err};
18
19type UndoEntry = (Vec<u8>, Option<(kevy_store::Value, Option<u64>)>);
22
23#[derive(Debug)]
27pub struct AtomicCtx<'a> {
28 inner: &'a mut Inner,
29 log: Vec<Vec<Vec<u8>>>,
30 undo: Vec<UndoEntry>,
35 touched: std::collections::HashSet<Vec<u8>>,
38}
39
40impl AtomicCtx<'_> {
41 pub fn set(&mut self, key: &[u8], value: &[u8]) -> bool {
46 self.snap(key);
47 let ok = self.inner.store.set(key, value.to_vec(), None, false, false);
48 self.log_arg(&[b"SET", key, value]);
49 ok
50 }
51
52 pub fn get(&mut self, key: &[u8]) -> KevyResult<Option<Vec<u8>>> {
54 self.inner.store.get(key).map(|opt| opt.as_deref().map(<[u8]>::to_vec)).map_err(store_err)
55 }
56
57 pub fn incr(&mut self, key: &[u8]) -> KevyResult<i64> {
59 self.snap(key);
60 let n = self.inner.store.incr_by(key, 1).map_err(store_err)?;
61 self.log_arg(&[b"INCR", key]);
62 Ok(n)
63 }
64
65 pub fn incr_by(&mut self, key: &[u8], delta: i64) -> KevyResult<i64> {
67 self.snap(key);
68 let n = self.inner.store.incr_by(key, delta).map_err(store_err)?;
69 let s = format!("{delta}");
70 self.log_arg(&[b"INCRBY", key, s.as_bytes()]);
71 Ok(n)
72 }
73
74 pub fn hset(&mut self, key: &[u8], pairs: &[(&[u8], &[u8])]) -> KevyResult<usize> {
78 self.snap(key);
79 let n = self.inner.store.hset(key, pairs).map_err(store_err)?;
80 let mut parts: Vec<&[u8]> = Vec::with_capacity(2 + pairs.len() * 2);
81 parts.push(b"HSET");
82 parts.push(key);
83 for (f, v) in pairs {
84 parts.push(f);
85 parts.push(v);
86 }
87 self.log_arg(&parts);
88 Ok(n)
89 }
90
91 pub fn hget(&mut self, key: &[u8], field: &[u8]) -> KevyResult<Option<Vec<u8>>> {
93 Ok(self.inner.store.hget(key, field).map_err(store_err)?.map(<[u8]>::to_vec))
94 }
95
96 pub fn hincrby(&mut self, key: &[u8], field: &[u8], delta: i64) -> KevyResult<i64> {
98 self.snap(key);
99 let n = self.inner.store.hincrby(key, field, delta).map_err(store_err)?;
100 let s = format!("{delta}");
101 self.log_arg(&[b"HINCRBY", key, field, s.as_bytes()]);
102 Ok(n)
103 }
104
105 pub fn zadd(&mut self, key: &[u8], pairs: &[(f64, &[u8])]) -> KevyResult<usize> {
109 self.snap(key);
110 let n = self.inner.store.zadd(key, pairs).map_err(store_err)?;
111 let score_strs: Vec<Vec<u8>> =
112 pairs.iter().map(|(s, _)| format!("{s}").into_bytes()).collect();
113 let mut parts: Vec<&[u8]> = Vec::with_capacity(2 + pairs.len() * 2);
114 parts.push(b"ZADD");
115 parts.push(key);
116 for (i, (_, m)) in pairs.iter().enumerate() {
117 parts.push(&score_strs[i]);
118 parts.push(m);
119 }
120 self.log_arg(&parts);
121 Ok(n)
122 }
123
124 pub fn zincrby(&mut self, key: &[u8], delta: f64, member: &[u8]) -> KevyResult<f64> {
126 self.snap(key);
127 let n = self.inner.store.zincrby(key, delta, member).map_err(store_err)?;
128 let s = format!("{delta}");
129 self.log_arg(&[b"ZINCRBY", key, s.as_bytes(), member]);
130 Ok(n)
131 }
132
133 pub fn zscore(&mut self, key: &[u8], member: &[u8]) -> KevyResult<Option<f64>> {
135 self.inner.store.zscore(key, member).map_err(store_err)
136 }
137
138 pub fn del(&mut self, keys: &[&[u8]]) -> usize {
144 for k in keys {
145 self.snap(k);
146 }
147 let n = self.inner.store.del(keys);
148 if n > 0 {
149 let mut argv: Vec<&[u8]> = Vec::with_capacity(1 + keys.len());
150 argv.push(b"DEL");
151 argv.extend_from_slice(keys);
152 self.log_arg(&argv);
153 }
154 n
155 }
156
157 pub fn exists(&mut self, keys: &[&[u8]]) -> usize {
159 keys.iter().filter(|k| self.inner.store.key_exists(k)).count()
160 }
161
162 pub fn hdel(&mut self, key: &[u8], fields: &[&[u8]]) -> KevyResult<usize> {
166 self.snap(key);
167 let removed = self.inner.store.hdel(key, fields).map_err(store_err)?;
168 if removed > 0 {
169 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + fields.len());
170 argv.push(b"HDEL");
171 argv.push(key);
172 argv.extend_from_slice(fields);
173 self.log_arg(&argv);
174 }
175 Ok(removed)
176 }
177
178 pub fn hgetall(&mut self, key: &[u8]) -> KevyResult<Vec<(Vec<u8>, Vec<u8>)>> {
181 let flat = self.inner.store.hgetall(key).map_err(store_err)?;
182 let mut out = Vec::with_capacity(flat.len() / 2);
183 let mut it = flat.into_iter();
184 while let (Some(f), Some(v)) = (it.next(), it.next()) {
185 out.push((f, v));
186 }
187 Ok(out)
188 }
189
190 pub fn hmget(&mut self, key: &[u8], fields: &[&[u8]]) -> KevyResult<Vec<Option<Vec<u8>>>> {
192 self.inner.store.hmget(key, fields).map_err(store_err)
193 }
194
195 pub fn hexists(&mut self, key: &[u8], field: &[u8]) -> KevyResult<bool> {
197 self.inner.store.hexists(key, field).map_err(store_err)
198 }
199
200 pub fn sadd(&mut self, key: &[u8], members: &[&[u8]]) -> KevyResult<usize> {
204 self.snap(key);
205 let added = self.inner.store.sadd(key, members).map_err(store_err)?;
206 if added > 0 {
207 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + members.len());
208 argv.push(b"SADD");
209 argv.push(key);
210 argv.extend_from_slice(members);
211 self.log_arg(&argv);
212 }
213 Ok(added)
214 }
215
216 pub fn srem(&mut self, key: &[u8], members: &[&[u8]]) -> KevyResult<usize> {
218 self.snap(key);
219 let removed = self.inner.store.srem(key, members).map_err(store_err)?;
220 if removed > 0 {
221 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + members.len());
222 argv.push(b"SREM");
223 argv.push(key);
224 argv.extend_from_slice(members);
225 self.log_arg(&argv);
226 }
227 Ok(removed)
228 }
229
230 pub fn lpush(&mut self, key: &[u8], values: &[&[u8]]) -> KevyResult<usize> {
234 self.snap(key);
235 let len = self.inner.store.lpush(key, values).map_err(store_err)?;
236 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + values.len());
237 argv.push(b"LPUSH");
238 argv.push(key);
239 argv.extend_from_slice(values);
240 self.log_arg(&argv);
241 Ok(len)
242 }
243
244 pub fn rpush(&mut self, key: &[u8], values: &[&[u8]]) -> KevyResult<usize> {
246 self.snap(key);
247 let len = self.inner.store.rpush(key, values).map_err(store_err)?;
248 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + values.len());
249 argv.push(b"RPUSH");
250 argv.push(key);
251 argv.extend_from_slice(values);
252 self.log_arg(&argv);
253 Ok(len)
254 }
255
256 pub fn zrem(&mut self, key: &[u8], members: &[&[u8]]) -> KevyResult<usize> {
260 self.snap(key);
261 let removed = self.inner.store.zrem(key, members).map_err(store_err)?;
262 if removed > 0 {
263 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + members.len());
264 argv.push(b"ZREM");
265 argv.push(key);
266 argv.extend_from_slice(members);
267 self.log_arg(&argv);
268 }
269 Ok(removed)
270 }
271
272 pub fn zcard(&mut self, key: &[u8]) -> KevyResult<usize> {
274 self.inner.store.zcard(key).map_err(store_err)
275 }
276
277 pub fn zadd_flags(
280 &mut self,
281 key: &[u8],
282 pairs: &[(f64, &[u8])],
283 flags: kevy_store::ZaddFlags,
284 ) -> KevyResult<kevy_store::ZaddReport> {
285 if !flags.valid() {
286 return Err(KevyError::InvalidInput("invalid ZADD flag combo".into()));
287 }
288 let rep = self.inner.store.zadd_flags(key, pairs, flags).map_err(store_err)?;
289 if !rep.applied.is_empty() {
290 let score_strs: Vec<Vec<u8>> =
291 rep.applied.iter().map(|(s, _)| format!("{s}").into_bytes()).collect();
292 let mut parts: Vec<&[u8]> = Vec::with_capacity(2 + rep.applied.len() * 2);
293 parts.push(b"ZADD");
294 parts.push(key);
295 for (i, (_, m)) in rep.applied.iter().enumerate() {
296 parts.push(&score_strs[i]);
297 parts.push(m);
298 }
299 self.log_arg(&parts);
300 }
301 Ok(rep)
302 }
303
304 pub fn smembers(&mut self, key: &[u8]) -> KevyResult<Vec<Vec<u8>>> {
313 self.inner.store.smembers(key).map_err(store_err)
314 }
315
316 pub fn sismember(&mut self, key: &[u8], member: &[u8]) -> KevyResult<bool> {
318 self.inner.store.sismember(key, member).map_err(store_err)
319 }
320
321 pub fn lrange(&mut self, key: &[u8], start: i64, stop: i64) -> KevyResult<Vec<Vec<u8>>> {
323 self.inner.store.lrange(key, start, stop).map_err(store_err)
324 }
325
326 pub fn llen(&mut self, key: &[u8]) -> KevyResult<usize> {
328 self.inner.store.llen(key).map_err(store_err)
329 }
330
331 pub fn scard(&mut self, key: &[u8]) -> KevyResult<usize> {
333 self.inner.store.scard(key).map_err(store_err)
334 }
335
336 pub fn zrangebyscore(
338 &mut self,
339 key: &[u8],
340 min: kevy_store::ScoreBound,
341 max: kevy_store::ScoreBound,
342 ) -> KevyResult<Vec<(Vec<u8>, f64)>> {
343 self.inner.store.zrange_by_score(key, min, max).map_err(store_err)
344 }
345
346 fn snap(&mut self, key: &[u8]) {
348 if self.touched.contains(key) {
349 return;
350 }
351 let prior = self.inner.store.clone_with_ttl(key);
352 self.touched.insert(key.to_vec());
353 self.undo.push((key.to_vec(), prior));
354 }
355
356 fn log_arg(&mut self, parts: &[&[u8]]) {
357 self.log.push(parts.iter().map(|p| p.to_vec()).collect());
358 }
359}
360
361impl Store {
362 pub fn atomic<R>(
387 &self,
388 body: impl FnOnce(&mut AtomicCtx<'_>) -> KevyResult<R>,
389 ) -> KevyResult<R> {
390 ensure_writable(self)?;
391 let mut g: RwLockWriteGuard<'_, Inner> = self.lock();
392 let mut ctx = AtomicCtx {
393 inner: &mut g,
394 log: Vec::new(),
395 undo: Vec::new(),
396 touched: std::collections::HashSet::new(),
397 };
398 let outcome = body(&mut ctx);
399 let log = std::mem::take(&mut ctx.log);
400 let undo = std::mem::take(&mut ctx.undo);
401 let r = match outcome {
402 Ok(r) => r,
403 Err(e) => {
404 rollback(&mut g, undo);
405 return Err(e);
406 }
407 };
408 commit_group(&mut g, log)?;
409 Ok(r)
410 }
411}
412
413#[cfg_attr(not(test), allow(dead_code))]
415pub(crate) const ATOMIC_OPS: &[&str] = &[
416 "SET",
417 "GET",
418 "INCR",
419 "INCRBY",
420 "HSET",
421 "HGET",
422 "HINCRBY",
423 "ZADD",
424 "ZINCRBY",
425 "ZSCORE",
426 "DEL",
427 "EXISTS",
428 "HDEL",
429 "HGETALL",
430 "HMGET",
431 "HEXISTS",
432 "SADD",
433 "SREM",
434 "LPUSH",
435 "RPUSH",
436 "ZREM",
437 "ZCARD",
438 "SMEMBERS",
439 "SISMEMBER",
440 "LRANGE",
441 "LLEN",
442 "SCARD",
443 "ZRANGEBYSCORE",
444];
445
446fn rollback(g: &mut Inner, undo: Vec<UndoEntry>) {
455 for (key, prior) in undo.into_iter().rev() {
456 match prior {
457 Some((value, ttl_ms)) => g.store.put_with_ttl(key, value, ttl_ms),
458 None => {
459 let k: &[u8] = &key;
460 g.store.del(&[k]);
461 }
462 }
463 }
464}
465
466fn commit_group(g: &mut Inner, log: Vec<Vec<Vec<u8>>>) -> KevyResult<()> {
472 #[cfg(feature = "persist")]
473 if let Some(aof) = g.aof.as_mut() {
474 aof.begin_group();
475 }
476 let mut commit = Ok(());
477 for entry in log {
478 let parts: Vec<&[u8]> = entry.iter().map(|v| v.as_slice()).collect();
479 commit = commit_write(g, &parts);
480 if commit.is_err() {
481 break;
482 }
483 }
484 #[cfg(feature = "persist")]
485 if let Some(aof) = g.aof.as_mut() {
486 let synced = aof.end_group().map_err(KevyError::from);
487 commit = commit.and(synced);
488 }
489 commit
490}