Skip to content

Commit e3793b8

Browse files
committed
Keep update cache unchanged on persist failure
Apply updates to a cloned object and only replace the cached entry after the backing store write succeeds. This finding was discovered by Project Loupe AI-Assisted-By: OpenAI Codex
1 parent 6560dff commit e3793b8

1 file changed

Lines changed: 26 additions & 15 deletions

File tree

src/data_store.rs

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -150,23 +150,23 @@ where
150150

151151
pub(crate) async fn update(&self, update: SO::Update) -> Result<DataStoreUpdateResult, Error> {
152152
let _guard = self.mutation_lock.lock().await;
153-
let (res, data_to_persist) = {
154-
let mut locked_objects = self.objects.lock().expect("lock");
155-
if let Some(object) = locked_objects.get_mut(&update.id()) {
156-
let updated = object.update(update);
157-
if updated {
158-
(DataStoreUpdateResult::Updated, Some(Self::encode_object(object)))
159-
} else {
160-
(DataStoreUpdateResult::Unchanged, None)
161-
}
162-
} else {
163-
(DataStoreUpdateResult::NotFound, None)
153+
let id = update.id();
154+
let updated_object = {
155+
let locked_objects = self.objects.lock().expect("lock");
156+
let Some(object) = locked_objects.get(&id) else {
157+
return Ok(DataStoreUpdateResult::NotFound);
158+
};
159+
let mut updated_object = object.clone();
160+
if !updated_object.update(update) {
161+
return Ok(DataStoreUpdateResult::Unchanged);
164162
}
163+
updated_object
165164
};
166-
if let Some((store_key, data)) = data_to_persist {
167-
self.persist_encoded(store_key, data).await?;
168-
}
169-
Ok(res)
165+
166+
self.persist(&updated_object).await?;
167+
let mut locked_objects = self.objects.lock().expect("lock");
168+
locked_objects.insert(id, updated_object);
169+
Ok(DataStoreUpdateResult::Updated)
170170
}
171171

172172
/// Returns in-memory objects matching `f`.
@@ -410,4 +410,15 @@ mod tests {
410410
assert_eq!(Err(Error::PersistenceFailed), data_store.insert_or_update(new_object).await);
411411
assert!(data_store.get(&new_id).is_none());
412412
}
413+
414+
#[tokio::test]
415+
async fn update_does_not_mutate_memory_if_persist_fails() {
416+
let id = TestObjectId { id: [42u8; 4] };
417+
let object = TestObject { id, data: [23u8; 3] };
418+
let data_store = new_failing_data_store(vec![object]);
419+
420+
let update = TestObjectUpdate { id, data: [24u8; 3] };
421+
assert_eq!(Err(Error::PersistenceFailed), data_store.update(update).await);
422+
assert_eq!(Some(object), data_store.get(&id));
423+
}
413424
}

0 commit comments

Comments
 (0)