launchdarkly_server_sdk/stores/
store.rs
1use crate::stores::store_types::{AllData, PatchTarget, StorageItem};
2use std::collections::HashMap;
3use thiserror::Error;
4
5use launchdarkly_server_sdk_evaluation::{Flag, Segment, Store, Versioned};
6
7use super::persistent_store::PersistentStoreError;
8
9#[non_exhaustive]
10#[derive(Debug, Error)]
11pub enum UpdateError {
12 #[error("invalid path: {0}")]
13 InvalidPath(String),
14
15 #[error("expected a {0}, got a {0}")]
16 InvalidTarget(String, String),
17
18 #[error("couldn't parse json as a flag or segment: {0}")]
19 ParseError(serde_json::Error),
20
21 #[error("underlying persistent store returned an error: {0}")]
22 PersistentStore(#[from] PersistentStoreError),
23}
24
25pub trait DataStore: Store + Send + Sync {
28 fn init(&mut self, new_data: AllData<Flag, Segment>);
29 fn all_flags(&self) -> HashMap<String, Flag>;
30 fn upsert(&mut self, key: &str, data: PatchTarget) -> Result<(), UpdateError>;
31 fn to_store(&self) -> &dyn Store;
32}
33
34pub struct InMemoryDataStore {
36 pub data: AllData<StorageItem<Flag>, StorageItem<Segment>>,
37}
38
39impl InMemoryDataStore {
40 pub fn new() -> Self {
41 Self {
42 data: AllData {
43 flags: HashMap::new(),
44 segments: HashMap::new(),
45 },
46 }
47 }
48
49 fn upsert_flag(&mut self, key: &str, item: StorageItem<Flag>) {
50 match self.data.flags.get(key) {
51 Some(existing) if existing.is_greater_than_or_equal(item.version()) => None,
52 _ => self.data.flags.insert(key.to_string(), item),
53 };
54 }
55
56 fn upsert_segment(&mut self, key: &str, item: StorageItem<Segment>) {
57 match self.data.segments.get(key) {
58 Some(existing) if existing.is_greater_than_or_equal(item.version()) => None,
59 _ => self.data.segments.insert(key.to_string(), item),
60 };
61 }
62}
63
64impl Store for InMemoryDataStore {
65 fn flag(&self, flag_key: &str) -> Option<Flag> {
66 match self.data.flags.get(flag_key) {
67 Some(StorageItem::Item(f)) => Some(f.clone()),
68 _ => None,
69 }
70 }
71
72 fn segment(&self, segment_key: &str) -> Option<Segment> {
73 match self.data.segments.get(segment_key) {
74 Some(StorageItem::Item(s)) => Some(s.clone()),
75 _ => None,
76 }
77 }
78}
79
80impl DataStore for InMemoryDataStore {
81 fn init(&mut self, new_data: AllData<Flag, Segment>) {
82 self.data = new_data.into();
83 debug!("data store has been updated with new flag data");
84 }
85
86 fn all_flags(&self) -> HashMap<String, Flag> {
87 self.data
88 .flags
89 .iter()
90 .filter_map(|(key, item)| match item {
91 StorageItem::Tombstone(_) => None,
92 StorageItem::Item(f) => Some((key.clone(), f.clone())),
93 })
94 .collect()
95 }
96
97 fn upsert(&mut self, key: &str, data: PatchTarget) -> Result<(), UpdateError> {
98 match data {
99 PatchTarget::Flag(item) => {
100 self.upsert_flag(key, item);
101 Ok(())
102 }
103 PatchTarget::Segment(item) => {
104 self.upsert_segment(key, item);
105 Ok(())
106 }
107 PatchTarget::Other(v) => Err(UpdateError::InvalidTarget(
108 "flag or segment".to_string(),
109 format!("{:?}", v),
110 )),
111 }
112 }
113
114 fn to_store(&self) -> &dyn Store {
115 self
116 }
117}
118
119impl Default for InMemoryDataStore {
120 fn default() -> Self {
121 Self::new()
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128 use crate::test_common::{basic_flag, basic_segment};
129 use maplit::hashmap;
130 use test_case::test_case;
131
132 fn basic_data() -> AllData<Flag, Segment> {
133 AllData {
134 flags: hashmap! {"flag-key".into() => basic_flag("flag-key")},
135 segments: hashmap! {"segment-key".into() => basic_segment("segment-key")},
136 }
137 }
138
139 #[test]
140 fn in_memory_can_be_initialized() {
141 let mut data_store = InMemoryDataStore::new();
142 assert!(data_store.flag("flag-key").is_none());
143 assert!(data_store.segment("segment-key").is_none());
144
145 data_store.init(basic_data());
146
147 assert_eq!(data_store.flag("flag-key").unwrap().key, "flag-key");
148 assert_eq!(
149 data_store.segment("segment-key").unwrap().key,
150 "segment-key"
151 );
152 }
153
154 #[test]
155 fn in_memory_can_return_all_flags() {
156 let mut data_store = InMemoryDataStore::new();
157
158 assert!(!data_store.all_flags().contains_key("flag-key"));
159 data_store.init(basic_data());
160 assert!(data_store.all_flags().contains_key("flag-key"));
161 }
162
163 #[test]
164 fn in_memory_patch_can_upsert_flag() {
165 let mut data_store = InMemoryDataStore::new();
166 data_store.init(basic_data());
167
168 assert!(data_store.flag("flag-does-not-exist").is_none());
170 let patch_target = PatchTarget::Flag(StorageItem::Item(basic_flag("flag-does-not-exist")));
171 let result = data_store.upsert("flag-does-not-exist", patch_target);
172 assert!(result.is_ok());
173 assert_eq!(
174 data_store.flag("flag-does-not-exist").unwrap().key,
175 "flag-does-not-exist"
176 );
177
178 let mut flag = basic_flag("new-key");
180 flag.version = 43;
181 let patch_target = PatchTarget::Flag(StorageItem::Item(flag));
182 let result = data_store.upsert("flag-key", patch_target);
183 assert!(result.is_ok());
184 assert_eq!(data_store.flag("flag-key").unwrap().key, "new-key");
185 }
186
187 #[test]
188 fn in_memory_patch_can_upsert_flag_deleted_flag() {
189 let mut data_store = InMemoryDataStore::new();
190 data_store.init(basic_data());
191
192 let mut flag = data_store.flag("flag-key").unwrap();
193 flag.version += 1;
194
195 assert!(data_store
196 .upsert(
197 "flag-key",
198 PatchTarget::Flag(StorageItem::Tombstone(flag.version))
199 )
200 .is_ok());
201 assert!(data_store.flag("flag-key").is_none());
202
203 flag.version -= 1;
204
205 let patch_target = PatchTarget::Flag(StorageItem::Item(flag.clone()));
206 assert!(data_store.upsert("flag-key", patch_target).is_ok());
207 assert!(data_store.flag("flag-key").is_none());
208
209 flag.version += 2;
210 let patch_target = PatchTarget::Flag(StorageItem::Item(flag));
211 assert!(data_store.upsert("flag-key", patch_target).is_ok());
212 assert!(data_store.flag("flag-key").is_some());
213 }
214
215 #[test_case(41, 42)]
216 #[test_case(43, 43)]
217 fn in_memory_patch_does_not_update_flag_with_older_version(
218 updated_version: u64,
219 expected_version: u64,
220 ) {
221 let mut data_store = InMemoryDataStore::new();
222 data_store.init(basic_data());
223
224 let mut flag = data_store.flag("flag-key").unwrap();
225 assert_eq!(42, flag.version);
226
227 flag.version = updated_version;
228
229 let patch_target = PatchTarget::Flag(StorageItem::Item(flag));
230 let result = data_store.upsert("flag-key", patch_target);
231 assert!(result.is_ok());
232
233 let flag = data_store.flag("flag-key").unwrap();
234 assert_eq!(expected_version, flag.version);
235 }
236
237 #[test]
238 fn in_memory_patch_can_upsert_segment() {
239 let mut data_store = InMemoryDataStore::new();
240 data_store.init(basic_data());
241
242 assert!(data_store.segment("segment-does-not-exist").is_none());
244 let patch_target =
245 PatchTarget::Segment(StorageItem::Item(basic_segment("segment-does-not-exist")));
246 let result = data_store.upsert("segment-does-not-exist", patch_target);
247 assert!(result.is_ok());
248 assert_eq!(
249 data_store.segment("segment-does-not-exist").unwrap().key,
250 "segment-does-not-exist"
251 );
252
253 let patch_target = PatchTarget::Segment(StorageItem::Item(basic_segment("new-key")));
255 let result = data_store.upsert("my-boolean-segment", patch_target);
256 assert!(result.is_ok());
257 assert_eq!(
258 data_store.segment("my-boolean-segment").unwrap().key,
259 "new-key"
260 );
261 }
262
263 #[test]
264 fn in_memory_patch_can_upsert_segment_deleted_segment() {
265 let mut data_store = InMemoryDataStore::new();
266 data_store.init(basic_data());
267
268 let mut segment = data_store.segment("segment-key").unwrap();
269 segment.version += 1;
270
271 assert!(data_store
272 .upsert(
273 "segment-key",
274 PatchTarget::Segment(StorageItem::Tombstone(segment.version))
275 )
276 .is_ok());
277 assert!(data_store.segment("segment-key").is_none());
278
279 segment.version -= 1;
280
281 let patch_target = PatchTarget::Segment(StorageItem::Item(segment.clone()));
282 assert!(data_store.upsert("segment-key", patch_target).is_ok());
283 assert!(data_store.segment("segment-key").is_none());
284
285 segment.version += 2;
286 let patch_target = PatchTarget::Segment(StorageItem::Item(segment));
287 assert!(data_store.upsert("segment-key", patch_target).is_ok());
288 assert!(data_store.segment("segment-key").is_some());
289 }
290
291 #[test_case(0, 1)]
292 #[test_case(2, 2)]
293 fn in_memory_patch_does_not_update_segment_with_older_version(
294 updated_version: u64,
295 expected_version: u64,
296 ) {
297 let mut data_store = InMemoryDataStore::new();
298 data_store.init(basic_data());
299
300 let mut segment = data_store.segment("segment-key").unwrap();
301 assert_eq!(1, segment.version);
302
303 segment.version = updated_version;
304
305 let patch_target = PatchTarget::Segment(StorageItem::Item(segment));
306 let result = data_store.upsert("segment-key", patch_target);
307 assert!(result.is_ok());
308
309 let segment = data_store.segment("segment-key").unwrap();
310 assert_eq!(expected_version, segment.version);
311 }
312
313 #[test]
314 fn in_memory_can_delete_flag() {
315 let mut data_store = InMemoryDataStore::new();
316 data_store.init(basic_data());
317
318 assert!(data_store.flag("flag-key").is_some());
319 assert!(data_store
320 .upsert("flag-key", PatchTarget::Flag(StorageItem::Tombstone(41)))
321 .is_ok());
322 assert!(data_store.flag("flag-key").is_some());
323
324 assert!(data_store
325 .upsert("flag-key", PatchTarget::Flag(StorageItem::Tombstone(42)))
326 .is_ok());
327 assert!(data_store.flag("flag-key").is_some());
328
329 data_store.init(basic_data());
330
331 assert!(data_store
332 .upsert("flag-key", PatchTarget::Flag(StorageItem::Tombstone(43)))
333 .is_ok());
334 assert!(data_store.flag("flag-key").is_none());
335 }
336
337 #[test]
338 fn in_memory_can_delete_segment() {
339 let mut data_store = InMemoryDataStore::new();
340 data_store.init(basic_data());
341
342 assert!(data_store.segment("segment-key").is_some());
343 assert!(data_store
344 .upsert(
345 "segment-key",
346 PatchTarget::Segment(StorageItem::Tombstone(0))
347 )
348 .is_ok());
349 assert!(data_store.segment("segment-key").is_some());
350
351 assert!(data_store
352 .upsert(
353 "segment-key",
354 PatchTarget::Segment(StorageItem::Tombstone(1))
355 )
356 .is_ok());
357 assert!(data_store.segment("segment-key").is_some());
358
359 data_store.init(basic_data());
360
361 assert!(data_store
362 .upsert(
363 "segment-key",
364 PatchTarget::Segment(StorageItem::Tombstone(2))
365 )
366 .is_ok());
367 assert!(data_store.segment("segment-key").is_none());
368 }
369}