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
47 .inner
48 .store
49 .set(key, value.to_vec(), None, false, false);
50 self.log_arg(&[b"SET", key, value]);
51 ok
52 }
53
54 pub fn get(&mut self, key: &[u8]) -> KevyResult<Option<Vec<u8>>> {
56 self.inner
57 .store
58 .get(key)
59 .map(|opt| opt.as_deref().map(<[u8]>::to_vec))
60 .map_err(store_err)
61 }
62
63 pub fn incr(&mut self, key: &[u8]) -> KevyResult<i64> {
65 self.snap(key);
66 let n = self.inner.store.incr_by(key, 1).map_err(store_err)?;
67 self.log_arg(&[b"INCR", key]);
68 Ok(n)
69 }
70
71 pub fn incr_by(&mut self, key: &[u8], delta: i64) -> KevyResult<i64> {
73 self.snap(key);
74 let n = self.inner.store.incr_by(key, delta).map_err(store_err)?;
75 let s = format!("{delta}");
76 self.log_arg(&[b"INCRBY", key, s.as_bytes()]);
77 Ok(n)
78 }
79
80 pub fn hset(&mut self, key: &[u8], pairs: &[(&[u8], &[u8])]) -> KevyResult<usize> {
84 self.snap(key);
85 let n = self.inner.store.hset(key, pairs).map_err(store_err)?;
86 let mut parts: Vec<&[u8]> = Vec::with_capacity(2 + pairs.len() * 2);
87 parts.push(b"HSET");
88 parts.push(key);
89 for (f, v) in pairs {
90 parts.push(f);
91 parts.push(v);
92 }
93 self.log_arg(&parts);
94 Ok(n)
95 }
96
97 pub fn hget(&mut self, key: &[u8], field: &[u8]) -> KevyResult<Option<Vec<u8>>> {
99 Ok(self
100 .inner
101 .store
102 .hget(key, field)
103 .map_err(store_err)?
104 .map(<[u8]>::to_vec))
105 }
106
107 pub fn hincrby(&mut self, key: &[u8], field: &[u8], delta: i64) -> KevyResult<i64> {
109 self.snap(key);
110 let n = self.inner.store.hincrby(key, field, delta).map_err(store_err)?;
111 let s = format!("{delta}");
112 self.log_arg(&[b"HINCRBY", key, field, s.as_bytes()]);
113 Ok(n)
114 }
115
116 pub fn zadd(&mut self, key: &[u8], pairs: &[(f64, &[u8])]) -> KevyResult<usize> {
120 self.snap(key);
121 let n = self.inner.store.zadd(key, pairs).map_err(store_err)?;
122 let score_strs: Vec<Vec<u8>> =
123 pairs.iter().map(|(s, _)| format!("{s}").into_bytes()).collect();
124 let mut parts: Vec<&[u8]> = Vec::with_capacity(2 + pairs.len() * 2);
125 parts.push(b"ZADD");
126 parts.push(key);
127 for (i, (_, m)) in pairs.iter().enumerate() {
128 parts.push(&score_strs[i]);
129 parts.push(m);
130 }
131 self.log_arg(&parts);
132 Ok(n)
133 }
134
135 pub fn zincrby(&mut self, key: &[u8], delta: f64, member: &[u8]) -> KevyResult<f64> {
137 self.snap(key);
138 let n = self.inner.store.zincrby(key, delta, member).map_err(store_err)?;
139 let s = format!("{delta}");
140 self.log_arg(&[b"ZINCRBY", key, s.as_bytes(), member]);
141 Ok(n)
142 }
143
144 pub fn zscore(&mut self, key: &[u8], member: &[u8]) -> KevyResult<Option<f64>> {
146 self.inner.store.zscore(key, member).map_err(store_err)
147 }
148
149 pub fn del(&mut self, keys: &[&[u8]]) -> usize {
155 for k in keys {
156 self.snap(k);
157 }
158 let n = self.inner.store.del(keys);
159 if n > 0 {
160 let mut argv: Vec<&[u8]> = Vec::with_capacity(1 + keys.len());
161 argv.push(b"DEL");
162 argv.extend_from_slice(keys);
163 self.log_arg(&argv);
164 }
165 n
166 }
167
168 pub fn exists(&mut self, keys: &[&[u8]]) -> usize {
170 keys.iter()
171 .filter(|k| self.inner.store.key_exists(k))
172 .count()
173 }
174
175 pub fn hdel(&mut self, key: &[u8], fields: &[&[u8]]) -> KevyResult<usize> {
179 self.snap(key);
180 let removed = self.inner.store.hdel(key, fields).map_err(store_err)?;
181 if removed > 0 {
182 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + fields.len());
183 argv.push(b"HDEL");
184 argv.push(key);
185 argv.extend_from_slice(fields);
186 self.log_arg(&argv);
187 }
188 Ok(removed)
189 }
190
191 pub fn hgetall(&mut self, key: &[u8]) -> KevyResult<Vec<(Vec<u8>, Vec<u8>)>> {
194 let flat = self.inner.store.hgetall(key).map_err(store_err)?;
195 let mut out = Vec::with_capacity(flat.len() / 2);
196 let mut it = flat.into_iter();
197 while let (Some(f), Some(v)) = (it.next(), it.next()) {
198 out.push((f, v));
199 }
200 Ok(out)
201 }
202
203 pub fn hmget(&mut self, key: &[u8], fields: &[&[u8]]) -> KevyResult<Vec<Option<Vec<u8>>>> {
205 self.inner.store.hmget(key, fields).map_err(store_err)
206 }
207
208 pub fn hexists(&mut self, key: &[u8], field: &[u8]) -> KevyResult<bool> {
210 self.inner.store.hexists(key, field).map_err(store_err)
211 }
212
213 pub fn sadd(&mut self, key: &[u8], members: &[&[u8]]) -> KevyResult<usize> {
217 self.snap(key);
218 let added = self.inner.store.sadd(key, members).map_err(store_err)?;
219 if added > 0 {
220 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + members.len());
221 argv.push(b"SADD");
222 argv.push(key);
223 argv.extend_from_slice(members);
224 self.log_arg(&argv);
225 }
226 Ok(added)
227 }
228
229 pub fn srem(&mut self, key: &[u8], members: &[&[u8]]) -> KevyResult<usize> {
231 self.snap(key);
232 let removed = self.inner.store.srem(key, members).map_err(store_err)?;
233 if removed > 0 {
234 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + members.len());
235 argv.push(b"SREM");
236 argv.push(key);
237 argv.extend_from_slice(members);
238 self.log_arg(&argv);
239 }
240 Ok(removed)
241 }
242
243 pub fn lpush(&mut self, key: &[u8], values: &[&[u8]]) -> KevyResult<usize> {
247 self.snap(key);
248 let len = self.inner.store.lpush(key, values).map_err(store_err)?;
249 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + values.len());
250 argv.push(b"LPUSH");
251 argv.push(key);
252 argv.extend_from_slice(values);
253 self.log_arg(&argv);
254 Ok(len)
255 }
256
257 pub fn rpush(&mut self, key: &[u8], values: &[&[u8]]) -> KevyResult<usize> {
259 self.snap(key);
260 let len = self.inner.store.rpush(key, values).map_err(store_err)?;
261 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + values.len());
262 argv.push(b"RPUSH");
263 argv.push(key);
264 argv.extend_from_slice(values);
265 self.log_arg(&argv);
266 Ok(len)
267 }
268
269 pub fn zrem(&mut self, key: &[u8], members: &[&[u8]]) -> KevyResult<usize> {
273 self.snap(key);
274 let removed = self.inner.store.zrem(key, members).map_err(store_err)?;
275 if removed > 0 {
276 let mut argv: Vec<&[u8]> = Vec::with_capacity(2 + members.len());
277 argv.push(b"ZREM");
278 argv.push(key);
279 argv.extend_from_slice(members);
280 self.log_arg(&argv);
281 }
282 Ok(removed)
283 }
284
285 pub fn zcard(&mut self, key: &[u8]) -> KevyResult<usize> {
287 self.inner.store.zcard(key).map_err(store_err)
288 }
289
290 pub fn zadd_flags(
293 &mut self,
294 key: &[u8],
295 pairs: &[(f64, &[u8])],
296 flags: kevy_store::ZaddFlags,
297 ) -> KevyResult<kevy_store::ZaddReport> {
298 if !flags.valid() {
299 return Err(KevyError::InvalidInput("invalid ZADD flag combo".into()));
300 }
301 let rep = self
302 .inner
303 .store
304 .zadd_flags(key, pairs, flags)
305 .map_err(store_err)?;
306 if !rep.applied.is_empty() {
307 let score_strs: Vec<Vec<u8>> = rep
308 .applied
309 .iter()
310 .map(|(s, _)| format!("{s}").into_bytes())
311 .collect();
312 let mut parts: Vec<&[u8]> = Vec::with_capacity(2 + rep.applied.len() * 2);
313 parts.push(b"ZADD");
314 parts.push(key);
315 for (i, (_, m)) in rep.applied.iter().enumerate() {
316 parts.push(&score_strs[i]);
317 parts.push(m);
318 }
319 self.log_arg(&parts);
320 }
321 Ok(rep)
322 }
323
324
325 pub fn smembers(&mut self, key: &[u8]) -> KevyResult<Vec<Vec<u8>>> {
334 self.inner.store.smembers(key).map_err(store_err)
335 }
336
337 pub fn sismember(&mut self, key: &[u8], member: &[u8]) -> KevyResult<bool> {
339 self.inner.store.sismember(key, member).map_err(store_err)
340 }
341
342 pub fn lrange(&mut self, key: &[u8], start: i64, stop: i64) -> KevyResult<Vec<Vec<u8>>> {
344 self.inner.store.lrange(key, start, stop).map_err(store_err)
345 }
346
347 pub fn llen(&mut self, key: &[u8]) -> KevyResult<usize> {
349 self.inner.store.llen(key).map_err(store_err)
350 }
351
352 pub fn scard(&mut self, key: &[u8]) -> KevyResult<usize> {
354 self.inner.store.scard(key).map_err(store_err)
355 }
356
357 pub fn zrangebyscore(
359 &mut self,
360 key: &[u8],
361 min: kevy_store::ScoreBound,
362 max: kevy_store::ScoreBound,
363 ) -> KevyResult<Vec<(Vec<u8>, f64)>> {
364 self.inner.store.zrange_by_score(key, min, max).map_err(store_err)
365 }
366
367 fn snap(&mut self, key: &[u8]) {
369 if self.touched.contains(key) {
370 return;
371 }
372 let prior = self.inner.store.clone_with_ttl(key);
373 self.touched.insert(key.to_vec());
374 self.undo.push((key.to_vec(), prior));
375 }
376
377 fn log_arg(&mut self, parts: &[&[u8]]) {
378 self.log.push(parts.iter().map(|p| p.to_vec()).collect());
379 }
380}
381
382impl Store {
383 pub fn atomic<R>(
408 &self,
409 body: impl FnOnce(&mut AtomicCtx<'_>) -> KevyResult<R>,
410 ) -> KevyResult<R> {
411 ensure_writable(self)?;
412 let mut g: RwLockWriteGuard<'_, Inner> = self.lock();
413 let mut ctx = AtomicCtx {
414 inner: &mut g,
415 log: Vec::new(),
416 undo: Vec::new(),
417 touched: std::collections::HashSet::new(),
418 };
419 let outcome = body(&mut ctx);
420 let log = std::mem::take(&mut ctx.log);
421 let undo = std::mem::take(&mut ctx.undo);
422 let r = match outcome {
423 Ok(r) => r,
424 Err(e) => {
425 rollback(&mut g, undo);
426 return Err(e);
427 }
428 };
429 commit_group(&mut g, log)?;
430 Ok(r)
431 }
432}
433
434#[cfg_attr(not(test), allow(dead_code))]
436pub(crate) const ATOMIC_OPS: &[&str] = &[
437 "SET", "GET", "INCR", "INCRBY", "HSET", "HGET", "HINCRBY", "ZADD",
438 "ZINCRBY", "ZSCORE", "DEL", "EXISTS", "HDEL", "HGETALL", "HMGET",
439 "HEXISTS", "SADD", "SREM", "LPUSH", "RPUSH", "ZREM", "ZCARD",
440 "SMEMBERS", "SISMEMBER", "LRANGE", "LLEN", "SCARD", "ZRANGEBYSCORE",
441];
442
443fn rollback(g: &mut Inner, undo: Vec<UndoEntry>) {
452 for (key, prior) in undo.into_iter().rev() {
453 match prior {
454 Some((value, ttl_ms)) => g.store.put_with_ttl(key, value, ttl_ms),
455 None => {
456 let k: &[u8] = &key;
457 g.store.del(&[k]);
458 }
459 }
460 }
461}
462
463fn commit_group(g: &mut Inner, log: Vec<Vec<Vec<u8>>>) -> KevyResult<()> {
469 #[cfg(feature = "persist")]
470 if let Some(aof) = g.aof.as_mut() {
471 aof.begin_group();
472 }
473 let mut commit = Ok(());
474 for entry in log {
475 let parts: Vec<&[u8]> = entry.iter().map(|v| v.as_slice()).collect();
476 commit = commit_write(g, &parts);
477 if commit.is_err() {
478 break;
479 }
480 }
481 #[cfg(feature = "persist")]
482 if let Some(aof) = g.aof.as_mut() {
483 let synced = aof.end_group().map_err(KevyError::from);
484 commit = commit.and(synced);
485 }
486 commit
487}