-
Notifications
You must be signed in to change notification settings - Fork 15
compare and swap design #53
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
Comments
Jumping in here as I have just created a draft implementation of CAS for AWS on the Spin platform, and the CAS interface seems like it could be modified to encourage correct usage. I wonder if this could use a form of "TypeState" pattern to ensure that users must call I perhaps am not seeing the use-case for calling |
Based on the research I did before working on this, several key value stores use a CAS ID of some kind to perform an atomic swap operation. The point of the design was so that you didn't have to call
This is the trade off of being generic. The only guarantee of this CAS operation of this interface is that if you I do like the idea of |
That's fair, I think (my personal) confusion lay in what is expected behavior here in stores that do require a CAS ID when you don't call current -- should implementations make an API call to get a CAS ID when CAS is created? Currently, in the Spin AWS and Azure implementations, a CAS ID is only fetched when users call Alternatively (and perhaps more in line with the design), we could make necessary idempotency calls when the CAS is created? If we go that route of making necessary idempotency API calls on CAS creation, then
Exactly this! It sounds like we're tentatively aligned on approach. I will dig into how this would work for all the stores implemented in Spin and report back if I see any hiccups in mapping the interface to each KV. Assuming that maps cleanly, should I create a PR here to update the WIT? |
I mapped the reworked interface to several of the implementations in Spin -- my only concern is that this rework would be a breaking change to the interface, but that might be unavoidable. We can get the best of both behaviors by having two functions (and this is more in line with what @pchickey proposed):
|
I think the new interface makes sense as long as it maps clearnly across all implementation.
Yes, that would be nice :) Could you please create a PR here to update the WITs and markdown files to reflect this new change, @ogghead ? |
Yep, sorry for the delay here. Thank you @ogghead! Breaking changes are fine here as we can rev |
Sounds good, I will aim to create a PR for these changes this week when I have some free time Update: Got this out locally with Spin implementations, will clean it up and make a PR this weekend. |
A
resource cas
has two methods: a constructor, and a methodcurrent: func -> result<option<list<u8>>, error>
for reading a value of a key, with an implicit borrow of the resource as its first argument. Calling this prior to aswap
doesn't seem useful - it is either a duplicate ofstore.get
, which incurs another database operation, or it must error. Thenswap: func(cas: cas, value<list<u8>>) -> result<_, cas-error>
takes ownership of theresource cas
, which means the methodcurrent
can't be called after it - the passed in resource will no longer be available to the caller, but some new one might be given in thecas-error
variant. So I don't think the constructor ofcas
is actually what we want, if the point is to put an optional accessor in the error.If you switched
swap
to just take theborrow<bucket>
andkey:string
instead or acas
, it wouldn't entirely solve the problem, which is that implementors need to know at the time theswap
call is made whether the value from the operation will be read - otherwise they have to transfer it from the database to the implementation unconditionally, and store it until thecas
resource is dropped.Instead, we could eliminate a lot of complexity by eliminating
cas-error
andresource cas
and just having two functions:The text was updated successfully, but these errors were encountered: