1use crate::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)]
39pub struct AtomicCtx<'a> {
40 inner: &'a mut Inner,
41 log: Vec<Vec<Vec<u8>>>,
42 undo: Vec<UndoEntry>,
47 touched: std::collections::HashSet<Vec<u8>>,
50}
51
52impl AtomicCtx<'_> {
53 pub fn set(&mut self, key: &[u8], value: &[u8]) -> bool {
58 self.snap(key);
59 let ok = self.inner.store.set(key, value.to_vec(), None, kevy_store::SetCondition::Always);
60 self.log_arg(&[b"SET", key, value]);
61 ok
62 }
63
64 pub fn get(&mut self, key: &[u8]) -> KevyResult<Option<Vec<u8>>> {
66 self.inner.store.get(key).map(|opt| opt.as_deref().map(<[u8]>::to_vec)).map_err(store_err)
67 }
68
69 pub fn incr(&mut self, key: &[u8]) -> KevyResult<i64> {
71 self.snap(key);
72 let n = self.inner.store.incr_by(key, 1).map_err(store_err)?;
73 self.log_arg(&[b"INCR", key]);
74 Ok(n)
75 }
76
77 pub fn incr_by(&mut self, key: &[u8], delta: i64) -> KevyResult<i64> {
79 self.snap(key);
80 let n = self.inner.store.incr_by(key, delta).map_err(store_err)?;
81 let s = format!("{delta}");
82 self.log_arg(&[b"INCRBY", key, s.as_bytes()]);
83 Ok(n)
84 }
85
86 pub fn hset(&mut self, key: &[u8], pairs: &[(&[u8], &[u8])]) -> KevyResult<usize> {
90 self.snap(key);
91 let n = self.inner.store.hset(key, pairs).map_err(store_err)?;
92 let mut parts: Vec<&[u8]> = Vec::with_capacity(2 + pairs.len() * 2);
93 parts.push(b"HSET");
94 parts.push(key);
95 for (f, v) in pairs {
96 parts.push(f);
97 parts.push(v);
98 }
99 self.log_arg(&parts);
100 Ok(n)
101 }
102
103 pub fn hget(&mut self, key: &[u8], field: &[u8]) -> KevyResult<Option<Vec<u8>>> {
105 Ok(self.inner.store.hget(key, field).map_err(store_err)?.map(<[u8]>::to_vec))
106 }
107
108 pub fn hincrby(&mut self, key: &[u8], field: &[u8], delta: i64) -> KevyResult<i64> {
110 self.snap(key);
111 let n = self.inner.store.hincrby(key, field, delta).map_err(store_err)?;
112 let s = format!("{delta}");
113 self.log_arg(&[b"HINCRBY", key, field, s.as_bytes()]);
114 Ok(n)
115 }
116
117 pub fn zadd(&mut self, key: &[u8], pairs: &[(f64, &[u8])]) -> KevyResult<usize> {
121 self.snap(key);
122 let n = self.inner.store.zadd(key, pairs).map_err(store_err)?;
123 let score_strs: Vec<Vec<u8>> =
124 pairs.iter().map(|(s, _)| format!("{s}").into_bytes()).collect();
125 let mut parts: Vec<&[u8]> = Vec::with_capacity(2 + pairs.len() * 2);
126 parts.push(b"ZADD");
127 parts.push(key);
128 for (i, (_, m)) in pairs.iter().enumerate() {
129 parts.push(&score_strs[i]);
130 parts.push(m);
131 }
132 self.log_arg(&parts);
133 Ok(n)
134 }
135
136 pub fn zincrby(&mut self, key: &[u8], delta: f64, member: &[u8]) -> KevyResult<f64> {
138 self.snap(key);
139 let n = self.inner.store.zincrby(key, delta, member).map_err(store_err)?;
140 let s = format!("{delta}");
141 self.log_arg(&[b"ZINCRBY", key, s.as_bytes(), member]);
142 Ok(n)
143 }
144
145 pub fn zscore(&mut self, key: &[u8], member: &[u8]) -> KevyResult<Option<f64>> {
147 self.inner.store.zscore(key, member).map_err(store_err)
148 }
149
150 pub fn del(&mut self, keys: &[&[u8]]) -> usize {
156 for k in keys {
157 self.snap(k);
158 }
159 let n = self.inner.store.del(keys);
160 if n > 0 {
161 let mut argv: Vec<&[u8]> = Vec::with_capacity(1 + keys.len());
162 argv.push(b"DEL");
163 argv.extend_from_slice(keys);
164 self.log_arg(&argv);
165 }
166 n
167 }
168
169 pub fn exists(&mut self, keys: &[&[u8]]) -> usize {
171 keys.iter().filter(|k| self.inner.store.key_exists(k)).count()
172 }
173
174 pub fn hdel(&mut self, key: &[u8], fields: &[&[u8]]) -> KevyResult<usize> {
178 self.snap(key);
179 let removed = self.inner.store.hdel(key, fields).map_err(store_err)?;
180 if removed > 0 {
181 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + fields.len());
182 argv.push(b"HDEL");
183 argv.push(key);
184 argv.extend_from_slice(fields);
185 self.log_arg(&argv);
186 }
187 Ok(removed)
188 }
189
190 pub fn hgetall(&mut self, key: &[u8]) -> KevyResult<Vec<(Vec<u8>, Vec<u8>)>> {
193 let flat = self.inner.store.hgetall(key).map_err(store_err)?;
194 let mut out = Vec::with_capacity(flat.len() / 2);
195 let mut it = flat.into_iter();
196 while let (Some(f), Some(v)) = (it.next(), it.next()) {
197 out.push((f, v));
198 }
199 Ok(out)
200 }
201
202 pub fn hmget(&mut self, key: &[u8], fields: &[&[u8]]) -> KevyResult<Vec<Option<Vec<u8>>>> {
204 self.inner.store.hmget(key, fields).map_err(store_err)
205 }
206
207 pub fn hexists(&mut self, key: &[u8], field: &[u8]) -> KevyResult<bool> {
209 self.inner.store.hexists(key, field).map_err(store_err)
210 }
211
212 pub fn sadd(&mut self, key: &[u8], members: &[&[u8]]) -> KevyResult<usize> {
216 self.snap(key);
217 let added = self.inner.store.sadd(key, members).map_err(store_err)?;
218 if added > 0 {
219 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + members.len());
220 argv.push(b"SADD");
221 argv.push(key);
222 argv.extend_from_slice(members);
223 self.log_arg(&argv);
224 }
225 Ok(added)
226 }
227
228 pub fn srem(&mut self, key: &[u8], members: &[&[u8]]) -> KevyResult<usize> {
230 self.snap(key);
231 let removed = self.inner.store.srem(key, members).map_err(store_err)?;
232 if removed > 0 {
233 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + members.len());
234 argv.push(b"SREM");
235 argv.push(key);
236 argv.extend_from_slice(members);
237 self.log_arg(&argv);
238 }
239 Ok(removed)
240 }
241
242 pub fn lpush(&mut self, key: &[u8], values: &[&[u8]]) -> KevyResult<usize> {
246 self.snap(key);
247 let len = self.inner.store.lpush(key, values).map_err(store_err)?;
248 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + values.len());
249 argv.push(b"LPUSH");
250 argv.push(key);
251 argv.extend_from_slice(values);
252 self.log_arg(&argv);
253 Ok(len)
254 }
255
256 pub fn rpush(&mut self, key: &[u8], values: &[&[u8]]) -> KevyResult<usize> {
258 self.snap(key);
259 let len = self.inner.store.rpush(key, values).map_err(store_err)?;
260 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + values.len());
261 argv.push(b"RPUSH");
262 argv.push(key);
263 argv.extend_from_slice(values);
264 self.log_arg(&argv);
265 Ok(len)
266 }
267
268 pub fn zrem(&mut self, key: &[u8], members: &[&[u8]]) -> KevyResult<usize> {
272 self.snap(key);
273 let removed = self.inner.store.zrem(key, members).map_err(store_err)?;
274 if removed > 0 {
275 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + members.len());
276 argv.push(b"ZREM");
277 argv.push(key);
278 argv.extend_from_slice(members);
279 self.log_arg(&argv);
280 }
281 Ok(removed)
282 }
283
284 pub fn zcard(&mut self, key: &[u8]) -> KevyResult<usize> {
286 self.inner.store.zcard(key).map_err(store_err)
287 }
288
289 pub fn zadd_flags(
292 &mut self,
293 key: &[u8],
294 pairs: &[(f64, &[u8])],
295 flags: kevy_store::ZaddFlags,
296 ) -> KevyResult<kevy_store::ZaddReport> {
297 let rep = self.inner.store.zadd_flags(key, pairs, flags).map_err(store_err)?;
298 if !rep.applied.is_empty() {
299 let score_strs: Vec<Vec<u8>> =
300 rep.applied.iter().map(|(s, _)| format!("{s}").into_bytes()).collect();
301 let mut parts: Vec<&[u8]> = Vec::with_capacity(2 + rep.applied.len() * 2);
302 parts.push(b"ZADD");
303 parts.push(key);
304 for (i, (_, m)) in rep.applied.iter().enumerate() {
305 parts.push(&score_strs[i]);
306 parts.push(m);
307 }
308 self.log_arg(&parts);
309 }
310 Ok(rep)
311 }
312
313 pub fn smembers(&mut self, key: &[u8]) -> KevyResult<Vec<Vec<u8>>> {
322 self.inner.store.smembers(key).map_err(store_err)
323 }
324
325 pub fn sismember(&mut self, key: &[u8], member: &[u8]) -> KevyResult<bool> {
327 self.inner.store.sismember(key, member).map_err(store_err)
328 }
329
330 pub fn lrange(&mut self, key: &[u8], start: i64, stop: i64) -> KevyResult<Vec<Vec<u8>>> {
332 self.inner.store.lrange(key, start, stop).map_err(store_err)
333 }
334
335 pub fn llen(&mut self, key: &[u8]) -> KevyResult<usize> {
337 self.inner.store.llen(key).map_err(store_err)
338 }
339
340 pub fn scard(&mut self, key: &[u8]) -> KevyResult<usize> {
342 self.inner.store.scard(key).map_err(store_err)
343 }
344
345 pub fn zrangebyscore(
347 &mut self,
348 key: &[u8],
349 min: kevy_store::ScoreBound,
350 max: kevy_store::ScoreBound,
351 ) -> KevyResult<Vec<(Vec<u8>, f64)>> {
352 self.inner.store.zrange_by_score(key, min, max).map_err(store_err)
353 }
354
355 fn snap(&mut self, key: &[u8]) {
357 if self.touched.contains(key) {
358 return;
359 }
360 let prior = self.inner.store.clone_with_ttl(key);
361 self.touched.insert(key.to_vec());
362 self.undo.push((key.to_vec(), prior));
363 }
364
365 fn log_arg(&mut self, parts: &[&[u8]]) {
366 self.log.push(parts.iter().map(|p| p.to_vec()).collect());
367 }
368}
369
370impl Store {
371 pub fn atomic<R>(
396 &self,
397 body: impl FnOnce(&mut AtomicCtx<'_>) -> KevyResult<R>,
398 ) -> KevyResult<R> {
399 ensure_writable(self)?;
400 let mut g: RwLockWriteGuard<'_, Inner> = self.lock();
401 let mut ctx = AtomicCtx {
402 inner: &mut g,
403 log: Vec::new(),
404 undo: Vec::new(),
405 touched: std::collections::HashSet::new(),
406 };
407 let outcome = body(&mut ctx);
408 let log = std::mem::take(&mut ctx.log);
409 let undo = std::mem::take(&mut ctx.undo);
410 let r = match outcome {
411 Ok(r) => r,
412 Err(e) => {
413 rollback(&mut g, undo);
414 return Err(e);
415 }
416 };
417 commit_group(&mut g, log)?;
418 Ok(r)
419 }
420}
421
422#[cfg_attr(not(test), allow(dead_code))]
424pub(crate) const ATOMIC_OPS: &[&str] = &[
425 "SET",
426 "GET",
427 "INCR",
428 "INCRBY",
429 "HSET",
430 "HGET",
431 "HINCRBY",
432 "ZADD",
433 "ZINCRBY",
434 "ZSCORE",
435 "DEL",
436 "EXISTS",
437 "HDEL",
438 "HGETALL",
439 "HMGET",
440 "HEXISTS",
441 "SADD",
442 "SREM",
443 "LPUSH",
444 "RPUSH",
445 "ZREM",
446 "ZCARD",
447 "SMEMBERS",
448 "SISMEMBER",
449 "LRANGE",
450 "LLEN",
451 "SCARD",
452 "ZRANGEBYSCORE",
453];
454
455fn rollback(g: &mut Inner, undo: Vec<UndoEntry>) {
464 for (key, prior) in undo.into_iter().rev() {
465 match prior {
466 Some((value, ttl_ms)) => g.store.put_with_ttl(key, value, ttl_ms),
467 None => {
468 let k: &[u8] = &key;
469 g.store.del(&[k]);
470 }
471 }
472 }
473}
474
475fn commit_group(g: &mut Inner, log: Vec<Vec<Vec<u8>>>) -> KevyResult<()> {
481 #[cfg(feature = "persist")]
482 if let Some(aof) = g.aof.as_mut() {
483 aof.begin_group();
484 }
485 let mut commit = Ok(());
486 for entry in log {
487 let parts: Vec<&[u8]> = entry.iter().map(|v| v.as_slice()).collect();
488 commit = commit_write(g, &parts);
489 if commit.is_err() {
490 break;
491 }
492 }
493 #[cfg(feature = "persist")]
494 if let Some(aof) = g.aof.as_mut() {
495 let synced = aof.end_group().map_err(crate::KevyError::from);
496 commit = commit.and(synced);
497 }
498 commit
499}