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