Skip to content

Commit

Permalink
Updated to handle CasError in wasm mapping, ensure new cas is returne…
Browse files Browse the repository at this point in the history
…d to client

Signed-off-by: Darwin Boersma <[email protected]>
  • Loading branch information
ogghead committed Nov 14, 2024
1 parent 1cc913c commit ca9edbb
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 20 deletions.
29 changes: 19 additions & 10 deletions crates/factor-key-value/src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,13 @@ impl wasi_keyvalue::atomics::HostCas for KeyValueDispatch {

#[async_trait]
impl wasi_keyvalue::atomics::Host for KeyValueDispatch {
fn convert_cas_error(
&mut self,
error: spin_world::wasi::keyvalue::atomics::CasError,
) -> std::result::Result<spin_world::wasi::keyvalue::atomics::CasError, anyhow::Error> {
Ok(error)
}

#[instrument(name = "spin_key_value.increment", skip(self, bucket, key, delta), err(level = Level::INFO), fields(otel.kind = "client"))]
async fn increment(
&mut self,
Expand All @@ -374,27 +381,29 @@ impl wasi_keyvalue::atomics::Host for KeyValueDispatch {
&mut self,
cas_res: Resource<atomics::Cas>,
value: Vec<u8>,
) -> Result<std::result::Result<(), CasError>> {
) -> Result<(), CasError> {
let cas_rep = cas_res.rep();
let cas = self
.get_cas(Resource::<Bucket>::new_own(cas_rep))
.map_err(|e| CasError::StoreError(atomics::Error::Other(e.to_string())))?;

match cas.swap(value).await {
Ok(_) => Ok(Ok(())),
Ok(_) => Ok(()),
Err(err) => match err {
SwapError::CasFailed(_) => {
let bucket = Resource::new_own(cas.bucket_rep().await);
let new_cas = self.new(bucket, cas.key().await).await?;
let new_cas = self
.new(bucket, cas.key().await)
.await
.map_err(CasError::StoreError)?;
let new_cas_rep = new_cas.rep();
self.current(Resource::new_own(new_cas_rep)).await?;
Err(anyhow::Error::new(CasError::CasFailed(Resource::new_own(
new_cas_rep,
))))
self.current(Resource::new_own(new_cas_rep))
.await
.map_err(CasError::StoreError)?;
let res = Resource::new_own(new_cas_rep);
Err(CasError::CasFailed(res))
}
SwapError::Other(msg) => Err(anyhow::Error::new(CasError::StoreError(
atomics::Error::Other(msg),
))),
SwapError::Other(msg) => Err(CasError::StoreError(atomics::Error::Other(msg))),
},
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/world/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ wasmtime::component::bindgen!({
"spin:postgres/postgres/error" => spin::postgres::postgres::Error,
"wasi:config/[email protected]/error" => wasi::config::store::Error,
"wasi:keyvalue/store/error" => wasi::keyvalue::store::Error,
"wasi:keyvalue/atomics/cas-error" => wasi::keyvalue::atomics::CasError,
},
trappable_imports: true,
});
Expand Down
18 changes: 9 additions & 9 deletions wit/deps/keyvalue-2024-10-17/atomic.wit
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,22 @@ interface atomics {

/// The error returned by a CAS operation
variant cas-error {
/// A store error occurred when performing the operation
store-error(error),
/// A store error occurred when performing the operation
store-error(error),
/// The CAS operation failed because the value was too old. This returns a new CAS handle
/// for easy retries. Implementors MUST return a CAS handle that has been updated to the
/// latest version or transaction.
cas-failed(cas),
cas-failed(cas),
}

/// A handle to a CAS (compare-and-swap) operation.
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>;
/// 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>;
}

/// Atomically increment the value associated with the key in the store by the given delta. It
Expand Down
2 changes: 1 addition & 1 deletion wit/deps/keyvalue-2024-10-17/world.wit
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package wasi: [email protected];
package wasi:keyvalue@0.2.0-draft2;

/// The `wasi:keyvalue/imports` world provides common APIs for interacting with key-value stores.
/// Components targeting this world will be able to do:
Expand Down

0 comments on commit ca9edbb

Please sign in to comment.