1use super::{KeySeq, PutOutcome, Store, StoreError};
9use serde_json::Value;
10use std::collections::BTreeMap;
11use std::sync::Mutex;
12use std::time::Duration;
13
14#[derive(Default)]
15struct Inner {
16 data: BTreeMap<String, BTreeMap<u64, Value>>,
18 fail_next: u32,
20 latency: Duration,
22 log: Vec<(String, String)>,
24}
25
26pub struct MemoryStore {
27 inner: Mutex<Inner>,
28}
29
30impl Default for MemoryStore {
31 fn default() -> Self {
32 Self::new()
33 }
34}
35
36impl MemoryStore {
37 pub fn new() -> MemoryStore {
38 MemoryStore {
39 inner: Mutex::new(Inner::default()),
40 }
41 }
42
43 pub fn fail_next(&self, n: u32) {
45 self.lock().fail_next = n;
46 }
47
48 pub fn set_latency(&self, latency: Duration) {
50 self.lock().latency = latency;
51 }
52
53 pub fn ops(&self) -> Vec<(String, String)> {
55 self.lock().log.clone()
56 }
57
58 pub fn len(&self) -> usize {
60 self.lock()
61 .data
62 .values()
63 .filter(|h| {
64 h.values()
65 .next_back()
66 .is_some_and(|v| !v.get("state").is_some_and(Value::is_null))
67 })
68 .count()
69 }
70
71 pub fn is_empty(&self) -> bool {
72 self.len() == 0
73 }
74
75 pub fn dump(&self) -> Vec<(String, u64)> {
77 self.lock()
78 .data
79 .iter()
80 .filter_map(|(k, h)| h.keys().next_back().map(|s| (k.clone(), *s)))
81 .collect()
82 }
83
84 fn lock(&self) -> std::sync::MutexGuard<'_, Inner> {
85 self.inner.lock().unwrap_or_else(|e| e.into_inner())
86 }
87
88 fn begin(&self, op: &str, key: &str) -> Result<std::sync::MutexGuard<'_, Inner>, StoreError> {
89 let mut g = self.lock();
90 g.log.push((op.to_string(), key.to_string()));
91 if g.fail_next > 0 {
92 g.fail_next -= 1;
93 return Err(StoreError::Io(format!("injected failure on {op} {key}")));
94 }
95 if !g.latency.is_zero() {
96 let d = g.latency;
97 drop(g);
98 std::thread::sleep(d);
99 g = self.lock();
100 }
101 Ok(g)
102 }
103}
104
105impl Store for MemoryStore {
106 fn put(&self, key: &str, seq: u64, envelope: &Value) -> Result<PutOutcome, StoreError> {
107 let mut g = self.begin("put", key)?;
108 let hist = g.data.entry(key.to_string()).or_default();
109 let latest = hist.keys().next_back().copied();
110 if let Some(l) = latest
111 && seq <= l
112 {
113 return Ok(PutOutcome::Conflict {
114 latest_seq: Some(l),
115 });
116 }
117 hist.insert(seq, envelope.clone());
118 Ok(PutOutcome::Ok)
119 }
120
121 fn get(&self, key: &str, seq: Option<u64>) -> Result<Option<Value>, StoreError> {
122 let g = self.begin("get", key)?;
123 let Some(hist) = g.data.get(key) else {
124 return Ok(None);
125 };
126 let picked = match seq {
127 Some(s) => hist.get(&s),
128 None => hist.values().next_back(),
129 };
130 Ok(picked
132 .filter(|v| !v.get("state").is_some_and(Value::is_null))
133 .cloned())
134 }
135
136 fn list(&self, prefix: &str) -> Result<Vec<KeySeq>, StoreError> {
137 let g = self.begin("list", prefix)?;
138 Ok(g.data
139 .iter()
140 .filter(|(k, h)| {
141 k.starts_with(prefix)
142 && h.values()
143 .next_back()
144 .is_some_and(|v| !v.get("state").is_some_and(Value::is_null))
145 })
146 .map(|(k, h)| KeySeq {
147 key: k.clone(),
148 seq: h.keys().next_back().copied(),
149 })
150 .collect())
151 }
152
153 fn delete(&self, key: &str) -> Result<(), StoreError> {
154 let mut g = self.begin("delete", key)?;
155 g.data.remove(key);
156 Ok(())
157 }
158
159 fn kind(&self) -> &'static str {
160 "memory"
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167 use serde_json::json;
168
169 #[test]
170 fn cas_history_list_delete_and_faults() {
171 let s = MemoryStore::new();
172 assert_eq!(
173 s.put("a/k", 1, &json!({"state": 1})).unwrap(),
174 PutOutcome::Ok
175 );
176 assert_eq!(
177 s.put("a/k", 2, &json!({"state": 2})).unwrap(),
178 PutOutcome::Ok
179 );
180 assert_eq!(
182 s.put("a/k", 2, &json!({"state": 9})).unwrap(),
183 PutOutcome::Conflict {
184 latest_seq: Some(2)
185 }
186 );
187 assert_eq!(
188 s.put("a/k", 1, &json!({"state": 9})).unwrap(),
189 PutOutcome::Conflict {
190 latest_seq: Some(2)
191 }
192 );
193 assert_eq!(s.get("a/k", None).unwrap(), Some(json!({"state": 2})));
195 assert_eq!(s.get("a/k", Some(1)).unwrap(), Some(json!({"state": 1})));
196 assert_eq!(s.get("a/k", Some(5)).unwrap(), None);
197 assert_eq!(s.get("a/none", None).unwrap(), None);
198 s.put("a/j", 1, &json!({"state": 0})).unwrap();
200 s.put("b/x", 1, &json!({"state": 0})).unwrap();
201 let l = s.list("a/").unwrap();
202 assert_eq!(l.len(), 2);
203 assert!(l.iter().any(|e| e.key == "a/k" && e.seq == Some(2)));
204 s.put("a/j", 2, &json!({"state": null})).unwrap();
206 assert_eq!(s.get("a/j", None).unwrap(), None);
207 assert_eq!(s.list("a/").unwrap().len(), 1);
208 s.delete("a/k").unwrap();
210 assert_eq!(s.get("a/k", None).unwrap(), None);
211 assert_eq!(
212 s.put("a/k", 1, &json!({"state": "again"})).unwrap(),
213 PutOutcome::Ok
214 );
215 s.fail_next(2);
217 assert!(matches!(s.get("a/k", None), Err(StoreError::Io(_))));
218 assert!(matches!(
219 s.put("a/k", 5, &json!({})),
220 Err(StoreError::Io(_))
221 ));
222 assert!(s.get("a/k", None).is_ok());
223 assert!(s.ops().iter().any(|(op, k)| op == "delete" && k == "a/k"));
224 }
225}