Skip to content

feat(*)!: Adds cas operation and fixes keys cursor#46

Merged
Mossaka merged 2 commits into
WebAssembly:mainfrom
thomastaylor312:feat/additional_feedback
Sep 23, 2024
Merged

feat(*)!: Adds cas operation and fixes keys cursor#46
Mossaka merged 2 commits into
WebAssembly:mainfrom
thomastaylor312:feat/additional_feedback

Conversation

@thomastaylor312
Copy link
Copy Markdown
Collaborator

This PR incorporates some feedback received on the current draft interface. First off, this changes the cursor for listing keys to be a string instead of a number in order to support more systems.

The second changes adds back in a CAS operation as multiple people had been asking for it. See the discussion in #44 for more context.

Because these are breaking changes and there are multiple draft implementations out there, I bumped this to draft2. Ideally we should be able to move to an RC soon with these changes added in

Closes #44
Closes #45

@thomastaylor312
Copy link
Copy Markdown
Collaborator Author

Marked this as a draft until we've finished any conversation in #44.

Also cc @kate-goldenring for visibility

Comment thread wit/atomic.wit Outdated
@thomastaylor312 thomastaylor312 marked this pull request as ready for review August 5, 2024 18:13
This PR incorporates some feedback received on the current draft interface.
First off, this changes the cursor for listing keys to be a string instead
of a number in order to support more systems.

The second changes adds back in a CAS operation as multiple people had
been asking for it. See the discussion in WebAssembly#44 for more context.

Because these are breaking changes and there are multiple draft
implementations out there, I bumped this to draft2. Ideally we should be
able to move to an RC soon with these changes added in

Closes WebAssembly#44
Closes WebAssembly#45

Signed-off-by: Taylor Thomas <taylor@cosmonic.com>
@thomastaylor312 thomastaylor312 force-pushed the feat/additional_feedback branch from e24291a to 9915d37 Compare August 9, 2024 20:45
@thomastaylor312
Copy link
Copy Markdown
Collaborator Author

I did a little noodling with @devigned and came up with something much cleaner. Curious what people think

When checking this operation across stores, I noticed that everything was
using signed numbers so you can increment or decrement as needed

Signed-off-by: Taylor Thomas <taylor@cosmonic.com>
@thomastaylor312
Copy link
Copy Markdown
Collaborator Author

Also, one last change after looking at the increment functionality across databases. The number and the delta should be signed integers so the counter can be decremented as needed

@kate-goldenring
Copy link
Copy Markdown
Contributor

With Spin, right now, we are only focused on implementing the store interface, so these additions are not anything we have to iterate on and by design LGTM.

Comment thread wit/atomic.wit
Comment on lines +25 to +32
resource cas {
/// Construct a new CAS operation. Implementors can map the underlying functionality
/// (transactions, versions, etc) as desired.
new: static func(bucket: borrow<bucket>, key: string) -> result<cas, error>;
/// Get the current value of the key (if it exists). This allows for avoiding reads if all
/// that is needed to ensure the atomicity of the operation
current: func() -> result<option<list<u8>>, error>;
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am curious to hear your motivations / rational behind the cas resource design? My feeling for that when I first saw it was complicated and more unintuitive than a simple operation like compare-and-swap: func(key: string, old: option<list<u8>>, new: option<list<u8>>) -> result<bool, error>;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This gives a lot more flexibility for how implementors can solve this. As seen in the survey of keyvalue stores I did, the most common things were transactions or revision_id-based options, along with some direct comparing and swapping. Representing this as a resource allows implementations to keep track of state for the operation. So for each of the 3 aforementioned types of operation, this is what is would roughly look like:

  • Transactions: An implementation can start a transaction and link it to a resource. You can still fetch the current value if you wish to do a comparison, but otherwise can use the guarantee of the transaction to ensure that you aren't clobbering a write
  • Revision IDs: Implementations can fetch the current revision of the key along with the value (or lazily load the value if current is called) and keep that revision ID around as its state. Then when the value is swapped it can use the revision ID
  • Direct compare and swap: The implementation wouldn't actually have any state but could fetch the value and do the comparison on the back end. As mentioned, this was by far the least used option among all the stores I surveyed

Forcing everyone to do a direct compare and swap means they have to a) fetch the value and b) send the other value. This could be much less efficient, especially if the values are large. It basically forces everything to implement it using a much less efficient way (that is much more vulnerable to the ABA problem you mentioned below) even though it is the least common option. The other options at least reduce the possibility of or eliminate the ABA problem as well, addressing your other comment)

Comment thread wit/atomic.wit

/// Perform the swap on a CAS operation. This consumes the CAS handle and returns an error if
/// the CAS operation failed.
swap: func(cas: cas, value: list<u8>) -> result<_, cas-error>;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A common problem for the implementation of compare-and-swap operation is that it suffers from the ABA problem. I am hoping to see documentation of this API to guide the host implementors on the expectation. Just mentioning "this API might suffer from the ABA problem" is fine to me.

Copy link
Copy Markdown
Collaborator

@devigned devigned left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm!

@Mossaka Mossaka merged commit 6630aef into WebAssembly:main Sep 23, 2024
@thomastaylor312 thomastaylor312 deleted the feat/additional_feedback branch September 23, 2024 22:41
@Mossaka
Copy link
Copy Markdown
Collaborator

Mossaka commented Oct 4, 2024

@thomastaylor312 can you add this to the Changelog in the README? I start to think about we should keep a changelog.md that follows https://keepachangelog.com/en/1.1.0/ format.

@thomastaylor312
Copy link
Copy Markdown
Collaborator Author

Any way we could just automate this? I know there are some tools out there

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The cursor in list-keys should probably be a string, not a u64 Test-and-set and other atomic operations?

5 participants