-
-
Notifications
You must be signed in to change notification settings - Fork 4
fix(service): Make compare_and_write idempotent #401
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -172,17 +172,12 @@ impl HighVolumeBackend for InMemoryBackend { | |
| write: TieredWrite, | ||
| ) -> Result<bool> { | ||
| let mut store = self.store.lock().unwrap(); | ||
| let actual = store.get(id); | ||
|
|
||
| let matches = match current { | ||
| None => !matches!(actual, Some(StoreEntry::Tombstone(_))), | ||
| Some(target) => matches!( | ||
| actual, | ||
| Some(StoreEntry::Tombstone(t)) if t.target == *target | ||
| ), | ||
| }; | ||
| let actual = store.get(id); | ||
| let matches_current = matches_redirect(actual, current); | ||
| let matches_next = matches_redirect(actual, write.target()); | ||
|
|
||
| if matches { | ||
| if matches_current { | ||
| match write { | ||
| TieredWrite::Tombstone(tombstone) => { | ||
| store.insert(id.clone(), StoreEntry::Tombstone(tombstone)); | ||
|
|
@@ -196,7 +191,18 @@ impl HighVolumeBackend for InMemoryBackend { | |
| } | ||
| } | ||
|
|
||
| Ok(matches) | ||
| Ok(matches_current || matches_next) | ||
cursor[bot] marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. InMemory backend skips writes Bigtable would applyMedium Severity The Additional Locations (1)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TL;DR: Both backends adhere to the spec. The detail is in the
Effectively, this is a race and the trait leaves this as implementation-defined behavior (not undefined behavior!). The contract that matters is that cleanup can happen, hence the trait leaves this up to implementors:
|
||
| } | ||
| } | ||
|
|
||
| /// Returns `true` if `entry` matches the expected tombstone redirect state. | ||
| /// | ||
| /// - `expected = None`: matches any non-tombstone (absent or inline object). | ||
| /// - `expected = Some(target)`: matches a tombstone whose redirect target equals `target`. | ||
| fn matches_redirect(entry: Option<&StoreEntry>, expected: Option<&ObjectId>) -> bool { | ||
| match expected { | ||
| None => matches!(entry, Some(StoreEntry::Object { .. }) | None), | ||
| Some(target) => matches!(entry, Some(StoreEntry::Tombstone(t)) if t.target == *target), | ||
| } | ||
| } | ||
|
|
||
|
|
||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The invert flag existed before and is a necessary evil. I tried to get rid of it, but
CheckAndMutateonly returnspredicate_matchedif at least one row matched the predicate. So it's not possible to create a negative match. The workaround is to usefalse_mutationsand invert the flag.I will eventually follow up by typing this properly. The predicates will become either a trait or a struct that has the necessary methods to generate the correct
CheckAndMutateRowRequestalong with the correct predicate matching.This can all be deferred to a follow-up.