diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c799f98f2508..ada3daffbc6f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -197,7 +197,6 @@ jobs: audit: ${{ steps.calculate.outputs.audit }} preview1-adapter: ${{ steps.calculate.outputs.preview1-adapter }} run-dwarf: ${{ steps.calculate.outputs.run-dwarf }} - run-wasi-keyvalue: ${{ steps.calculate.outputs.run-wasi-keyvalue }} steps: - uses: actions/checkout@v4 - id: calculate @@ -242,9 +241,6 @@ jobs: if grep -q debug names.log; then echo run-dwarf=true >> $GITHUB_OUTPUT fi - if grep -q wasi-keyvalue names.log; then - echo run-wasi-keyvalue=true >> $GITHUB_OUTPUT - fi fi matrix="$(node ./ci/build-test-matrix.js ./commits.log ./names.log $run_full)" echo "test-matrix={\"include\":$(echo $matrix)}" >> $GITHUB_OUTPUT @@ -260,7 +256,6 @@ jobs: echo audit=true >> $GITHUB_OUTPUT echo preview1-adapter=true >> $GITHUB_OUTPUT echo run-dwarf=true >> $GITHUB_OUTPUT - echo run-wasi-keyvalue=true >> $GITHUB_OUTPUT fi # Build all documentation of Wasmtime, including the C API documentation, @@ -742,44 +737,6 @@ jobs: env: GH_TOKEN: ${{ github.token }} - # Test the `wasmtime-wasi-keyvalue` crate. Split out from the main tests - # because it needs additional database service. - test_wasi_keyvalue: - name: Test wasi-keyvalue - runs-on: ubuntu-latest - needs: determine - if: needs.determine.outputs.run-wasi-keyvalue - # Setup redis server - services: - redis: - # Docker Hub image - image: redis - # Set health checks to wait until redis has started - options: >- - --health-cmd "redis-cli ping" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - # Maps port 6379 on service container to the host - - 6379:6379 - steps: - - uses: actions/checkout@v4 - with: - submodules: true - - uses: ./.github/actions/install-rust - # Install Rust targets - - run: rustup target add wasm32-wasi - - run: cargo test --all-features -p wasmtime-wasi-keyvalue - env: - RUST_BACKTRACE: 1 - - # common logic to cancel the entire run if this job fails - - run: gh run cancel ${{ github.run_id }} - if: failure() && github.event_name != 'pull_request' - env: - GH_TOKEN: ${{ github.token }} - # Test the `wasmtime-fuzzing` crate. Split out from the main tests because # `--all-features` brings in OCaml, which is a pain to get setup for all # targets. diff --git a/Cargo.lock b/Cargo.lock index 4bd51df868de..b7d2dbc8c558 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -477,20 +477,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes", - "futures-core", - "memchr", - "pin-project-lite", - "tokio", - "tokio-util", -] - [[package]] name = "component-fuzz-util" version = "0.0.0" @@ -2247,27 +2233,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "redis" -version = "0.25.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0d7a6955c7511f60f3ba9e86c6d02b3c3f144f8c24b288d1f4e18074ab8bbec" -dependencies = [ - "async-trait", - "bytes", - "combine", - "futures-util", - "itoa", - "percent-encoding", - "pin-project-lite", - "ryu", - "sha1_smol", - "socket2", - "tokio", - "tokio-util", - "url", -] - [[package]] name = "redox_syscall" version = "0.2.13" @@ -2527,12 +2492,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha1_smol" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" - [[package]] name = "sha2" version = "0.10.2" @@ -3925,11 +3884,8 @@ name = "wasmtime-wasi-keyvalue" version = "24.0.0" dependencies = [ "anyhow", - "async-trait", - "redis", "test-programs-artifacts", "tokio", - "url", "wasmtime", "wasmtime-wasi", ] diff --git a/Cargo.toml b/Cargo.toml index c43bf4961f0d..064bfe2990d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -332,7 +332,6 @@ criterion = { version = "0.5.0", default-features = false, features = ["html_rep rustc-hash = "1.1.0" libtest-mimic = "0.7.0" semver = { version = "1.0.17", default-features = false } -redis = "0.25.4" # ============================================================================= # @@ -407,7 +406,7 @@ wasi-nn = ["dep:wasmtime-wasi-nn"] wasi-threads = ["dep:wasmtime-wasi-threads", "threads"] wasi-http = ["component-model", "dep:wasmtime-wasi-http", "dep:tokio", "dep:hyper"] wasi-runtime-config = ["dep:wasmtime-wasi-runtime-config"] -wasi-keyvalue = ["dep:wasmtime-wasi-keyvalue", "wasmtime-wasi-keyvalue/redis"] +wasi-keyvalue = ["dep:wasmtime-wasi-keyvalue"] pooling-allocator = ["wasmtime/pooling-allocator", "wasmtime-cli-flags/pooling-allocator"] component-model = [ "wasmtime/component-model", diff --git a/ci/run-tests.sh b/ci/run-tests.sh index 1822a3fd07cb..274d35232177 100755 --- a/ci/run-tests.sh +++ b/ci/run-tests.sh @@ -12,9 +12,6 @@ # # - wasm-spec-interpreter: brings in OCaml which is a pain to configure for all # targets, tested as part of the wastime-fuzzing CI job. -# -# - wasmtime-wasi-keyvalue: additional database service dependencies, needs its -# own CI job. cargo test \ --workspace \ @@ -23,5 +20,4 @@ cargo test \ --exclude wasmtime-wasi-nn \ --exclude wasmtime-fuzzing \ --exclude wasm-spec-interpreter \ - --exclude wasmtime-wasi-keyvalue \ $@ diff --git a/crates/cli-flags/src/lib.rs b/crates/cli-flags/src/lib.rs index 6f65e6de0888..f172d254115f 100644 --- a/crates/cli-flags/src/lib.rs +++ b/crates/cli-flags/src/lib.rs @@ -329,15 +329,6 @@ wasmtime_option_group! { pub runtime_config_var: Vec, /// Preset data for the In-Memory provider of WASI key-value API. pub keyvalue_in_memory_data: Vec, - /// Grant access to the given Redis host for the Redis provider of WASI - /// key-value API. - pub keyvalue_redis_host: Vec, - /// Sets the connection timeout parameter for the Redis provider of WASI - /// key-value API. - pub keyvalue_redis_connection_timeout: Option, - /// Sets the response timeout parameter for the Redis provider of WASI - /// key-value API. - pub keyvalue_redis_response_timeout: Option, } enum Wasi { diff --git a/crates/test-programs/src/bin/keyvalue_main.rs b/crates/test-programs/src/bin/keyvalue_main.rs index 486f59e19a17..5fc884a82860 100644 --- a/crates/test-programs/src/bin/keyvalue_main.rs +++ b/crates/test-programs/src/bin/keyvalue_main.rs @@ -1,13 +1,9 @@ use test_programs::keyvalue::wasi::keyvalue::{atomics, batch, store}; fn main() { - let identifier = std::env::var("IDENTIFIER").unwrap_or("".to_string()); - let bucket = store::open(&identifier).unwrap(); + let bucket = store::open("").unwrap(); - if identifier != "" { - // for In-Memory provider, we have preset this data - assert_eq!(atomics::increment(&bucket, "atomics_key", 5).unwrap(), 5); - } + // we have preset this `atomics_key` with value 5 assert_eq!(atomics::increment(&bucket, "atomics_key", 1).unwrap(), 6); let resp = bucket.list_keys(None).unwrap(); diff --git a/crates/wasi-keyvalue/Cargo.toml b/crates/wasi-keyvalue/Cargo.toml index 7802af6bfd82..a2d4ef361277 100644 --- a/crates/wasi-keyvalue/Cargo.toml +++ b/crates/wasi-keyvalue/Cargo.toml @@ -12,15 +12,9 @@ workspace = true [dependencies] anyhow = { workspace = true } -wasmtime = { workspace = true, features = ["runtime", "async", "component-model"] } -wasmtime-wasi = { workspace = true } -async-trait = { workspace = true } -url = { workspace = true } -redis = { workspace = true, optional = true, features = ["tokio-comp"] } +wasmtime = { workspace = true, features = ["runtime", "component-model"] } [dev-dependencies] test-programs-artifacts = { workspace = true } +wasmtime-wasi = { workspace = true } tokio = { workspace = true, features = ["macros"] } - -[features] -redis = ["dep:redis"] diff --git a/crates/wasi-keyvalue/src/bindings.rs b/crates/wasi-keyvalue/src/bindings.rs deleted file mode 100644 index 56091c238783..000000000000 --- a/crates/wasi-keyvalue/src/bindings.rs +++ /dev/null @@ -1,26 +0,0 @@ -wasmtime::component::bindgen!({ - path: "wit", - world: "wasi:keyvalue/imports", - trappable_imports: true, - async: true, - with: { - "wasi:keyvalue/store/bucket": crate::Bucket, - }, - trappable_error_type: { - "wasi:keyvalue/store/error" => crate::Error, - }, -}); - -pub(crate) mod sync { - wasmtime::component::bindgen!({ - path: "wit", - world: "wasi:keyvalue/imports", - trappable_imports: true, - with: { - "wasi:keyvalue/store/bucket": crate::Bucket, - }, - trappable_error_type: { - "wasi:keyvalue/store/error" => crate::Error, - }, - }); -} diff --git a/crates/wasi-keyvalue/src/lib.rs b/crates/wasi-keyvalue/src/lib.rs index 46addae254ce..c45e501f8074 100644 --- a/crates/wasi-keyvalue/src/lib.rs +++ b/crates/wasi-keyvalue/src/lib.rs @@ -6,16 +6,13 @@ //! //! Currently supported storage backends: //! * In-Memory (empty identifier) -//! * Redis, supported identifier format: -//! * `redis://[][:@][:port][/]` -//! * `redis+unix:///[?db=[&pass=][&user=]]` //! //! # Examples //! //! The usage of this crate is very similar to other WASI API implementations //! such as [wasi:cli] and [wasi:http]. //! -//! A common scenario is accessing redis in a [wasi:cli] component. +//! A common scenario is accessing KV store in a [wasi:cli] component. //! A standalone example of doing all this looks like: //! //! ``` @@ -41,7 +38,7 @@ //! let mut linker = Linker::::new(&engine); //! wasmtime_wasi::add_to_linker_async(&mut linker)?; //! // add `wasi-runtime-config` world's interfaces to the linker -//! wasmtime_wasi_keyvalue::add_to_linker_async(&mut linker, |h: &mut Ctx| { +//! wasmtime_wasi_keyvalue::add_to_linker(&mut linker, |h: &mut Ctx| { //! WasiKeyValue::new(&h.wasi_keyvalue_ctx, &mut h.table) //! })?; //! @@ -68,17 +65,24 @@ #![deny(missing_docs)] -mod bindings; -mod provider; +mod generated { + wasmtime::component::bindgen!({ + path: "wit", + world: "wasi:keyvalue/imports", + trappable_imports: true, + with: { + "wasi:keyvalue/store/bucket": crate::Bucket, + }, + trappable_error_type: { + "wasi:keyvalue/store/error" => crate::Error, + }, + }); +} -use self::bindings::{sync::wasi::keyvalue as keyvalue_sync, wasi::keyvalue}; +use self::generated::wasi::keyvalue; use anyhow::Result; -use async_trait::async_trait; use std::collections::HashMap; -use std::fmt::Display; -use url::Url; use wasmtime::component::{Resource, ResourceTable, ResourceTableError}; -use wasmtime_wasi::runtime::in_tokio; #[doc(hidden)] pub enum Error { @@ -93,52 +97,15 @@ impl From for Error { } } -pub(crate) fn to_other_error(e: impl Display) -> Error { - Error::Other(e.to_string()) -} - #[doc(hidden)] pub struct Bucket { - inner: Box, -} - -#[async_trait] -trait Host { - async fn get(&mut self, key: String) -> Result>, Error>; - - async fn set(&mut self, key: String, value: Vec) -> Result<(), Error>; - - async fn delete(&mut self, key: String) -> Result<(), Error>; - - async fn exists(&mut self, key: String) -> Result; - - async fn list_keys( - &mut self, - cursor: Option, - ) -> Result; - - async fn increment(&mut self, key: String, delta: u64) -> Result; - - async fn get_many( - &mut self, - keys: Vec, - ) -> Result)>>, Error>; - - async fn set_many(&mut self, key_values: Vec<(String, Vec)>) -> Result<(), Error>; - - async fn delete_many(&mut self, keys: Vec) -> Result<(), Error>; + in_memory_data: HashMap>, } /// Builder-style structure used to create a [`WasiKeyValueCtx`]. #[derive(Default)] pub struct WasiKeyValueCtxBuilder { in_memory_data: HashMap>, - #[cfg(feature = "redis")] - allowed_redis_hosts: Vec, - #[cfg(feature = "redis")] - redis_connection_timeout: Option, - #[cfg(feature = "redis")] - redis_response_timeout: Option, } impl WasiKeyValueCtxBuilder { @@ -161,52 +128,10 @@ impl WasiKeyValueCtxBuilder { self } - /// Appends a list of Redis hosts to the allow-listed set each component gets - /// access to. It can be in the format `[:port]` or a unix domain - /// socket path. - /// - /// # Examples - /// - /// ``` - /// use wasmtime_wasi_keyvalue::WasiKeyValueCtxBuilder; - /// - /// # fn main() { - /// let ctx = WasiKeyValueCtxBuilder::new() - /// .allow_redis_hosts(&["localhost:1234", "/var/run/redis.sock"]) - /// .build(); - /// # } - /// ``` - #[cfg(feature = "redis")] - pub fn allow_redis_hosts(mut self, hosts: &[impl AsRef]) -> Self { - self.allowed_redis_hosts - .extend(hosts.iter().map(|h| h.as_ref().to_owned())); - self - } - - /// Sets the connection timeout parameter for the Redis provider. - #[cfg(feature = "redis")] - pub fn redis_connection_timeout(mut self, t: std::time::Duration) -> Self { - self.redis_connection_timeout = Some(t); - self - } - - /// Sets the response timeout parameter for the Redis provider. - #[cfg(feature = "redis")] - pub fn redis_response_timeout(mut self, t: std::time::Duration) -> Self { - self.redis_response_timeout = Some(t); - self - } - /// Uses the configured context so far to construct the final [`WasiKeyValueCtx`]. pub fn build(self) -> WasiKeyValueCtx { WasiKeyValueCtx { in_memory_data: self.in_memory_data, - #[cfg(feature = "redis")] - allowed_redis_hosts: self.allowed_redis_hosts, - #[cfg(feature = "redis")] - redis_connection_timeout: self.redis_connection_timeout, - #[cfg(feature = "redis")] - redis_response_timeout: self.redis_response_timeout, } } } @@ -214,12 +139,6 @@ impl WasiKeyValueCtxBuilder { /// Capture the state necessary for use in the `wasi-keyvalue` API implementation. pub struct WasiKeyValueCtx { in_memory_data: HashMap>, - #[cfg(feature = "redis")] - allowed_redis_hosts: Vec, - #[cfg(feature = "redis")] - redis_connection_timeout: Option, - #[cfg(feature = "redis")] - redis_response_timeout: Option, } impl WasiKeyValueCtx { @@ -227,19 +146,6 @@ impl WasiKeyValueCtx { pub fn builder() -> WasiKeyValueCtxBuilder { WasiKeyValueCtxBuilder::new() } - - #[cfg(feature = "redis")] - fn allow_redis_host(&self, u: &Url) -> bool { - let host = match u.host() { - Some(h) => match u.port() { - Some(port) => format!("{}:{}", h, port), - None => h.to_string(), - }, - // unix domain socket path - None => u.path().to_string(), - }; - self.allowed_redis_hosts.contains(&host) - } } /// A wrapper capturing the needed internal `wasi-keyvalue` state. @@ -255,47 +161,12 @@ impl<'a> WasiKeyValue<'a> { } } -#[async_trait] impl keyvalue::store::Host for WasiKeyValue<'_> { - async fn open(&mut self, identifier: String) -> Result, Error> { - if identifier == "" { - return Ok(self.table.push(Bucket { - inner: Box::new(provider::inmemory::InMemory::new( - self.ctx.in_memory_data.clone(), - )), - })?); - } - - let u = Url::parse(&identifier).map_err(to_other_error)?; - match u.scheme() { - "redis" | "redis+unix" => { - #[cfg(not(feature = "redis"))] - { - return Err(Error::Other( - "Cannot enable Redis support when the crate is not compiled with this feature." - .to_string(), - )); - } - #[cfg(feature = "redis")] - { - if !self.ctx.allow_redis_host(&u) { - return Err(Error::Other(format!( - "the identifier {} is not in the allowed list", - identifier - ))); - } - - let host = provider::redis::open( - identifier, - self.ctx.redis_response_timeout, - self.ctx.redis_connection_timeout, - ) - .await?; - Ok(self.table.push(Bucket { - inner: Box::new(host), - })?) - } - } + fn open(&mut self, identifier: String) -> Result, Error> { + match identifier.as_str() { + "" => Ok(self.table.push(Bucket { + in_memory_data: self.ctx.in_memory_data.clone(), + })?), _ => Err(Error::NoSuchStore), } } @@ -309,44 +180,42 @@ impl keyvalue::store::Host for WasiKeyValue<'_> { } } -#[async_trait] impl keyvalue::store::HostBucket for WasiKeyValue<'_> { - async fn get( - &mut self, - bucket: Resource, - key: String, - ) -> Result>, Error> { + fn get(&mut self, bucket: Resource, key: String) -> Result>, Error> { let bucket = self.table.get_mut(&bucket)?; - bucket.inner.get(key).await + Ok(bucket.in_memory_data.get(&key).cloned()) } - async fn set( - &mut self, - bucket: Resource, - key: String, - value: Vec, - ) -> Result<(), Error> { + fn set(&mut self, bucket: Resource, key: String, value: Vec) -> Result<(), Error> { let bucket = self.table.get_mut(&bucket)?; - bucket.inner.set(key, value).await + bucket.in_memory_data.insert(key, value); + Ok(()) } - async fn delete(&mut self, bucket: Resource, key: String) -> Result<(), Error> { + fn delete(&mut self, bucket: Resource, key: String) -> Result<(), Error> { let bucket = self.table.get_mut(&bucket)?; - bucket.inner.delete(key).await + bucket.in_memory_data.remove(&key); + Ok(()) } - async fn exists(&mut self, bucket: Resource, key: String) -> Result { + fn exists(&mut self, bucket: Resource, key: String) -> Result { let bucket = self.table.get_mut(&bucket)?; - bucket.inner.exists(key).await + Ok(bucket.in_memory_data.contains_key(&key)) } - async fn list_keys( + fn list_keys( &mut self, bucket: Resource, cursor: Option, ) -> Result { let bucket = self.table.get_mut(&bucket)?; - bucket.inner.list_keys(cursor).await + let keys: Vec = bucket.in_memory_data.keys().cloned().collect(); + let cursor = cursor.unwrap_or(0) as usize; + let keys_slice = &keys[cursor..]; + Ok(keyvalue::store::KeyResponse { + keys: keys_slice.to_vec(), + cursor: None, + }) } fn drop(&mut self, bucket: Resource) -> Result<()> { @@ -355,56 +224,69 @@ impl keyvalue::store::HostBucket for WasiKeyValue<'_> { } } -#[async_trait] impl keyvalue::atomics::Host for WasiKeyValue<'_> { - async fn increment( + fn increment( &mut self, bucket: Resource, key: String, delta: u64, ) -> Result { let bucket = self.table.get_mut(&bucket)?; - bucket.inner.increment(key, delta).await + let value = bucket + .in_memory_data + .entry(key.clone()) + .or_insert("0".to_string().into_bytes()); + let current_value = String::from_utf8(value.clone()) + .map_err(|e| Error::Other(e.to_string()))? + .parse::() + .map_err(|e| Error::Other(e.to_string()))?; + let new_value = current_value + delta; + *value = new_value.to_string().into_bytes(); + Ok(new_value) } } -#[async_trait] impl keyvalue::batch::Host for WasiKeyValue<'_> { - async fn get_many( + fn get_many( &mut self, bucket: Resource, keys: Vec, ) -> Result)>>, Error> { let bucket = self.table.get_mut(&bucket)?; - bucket.inner.get_many(keys).await + Ok(keys + .into_iter() + .map(|key| { + bucket + .in_memory_data + .get(&key) + .map(|value| (key.clone(), value.clone())) + }) + .collect()) } - async fn set_many( + fn set_many( &mut self, bucket: Resource, key_values: Vec<(String, Vec)>, ) -> Result<(), Error> { let bucket = self.table.get_mut(&bucket)?; - bucket.inner.set_many(key_values).await + for (key, value) in key_values { + bucket.in_memory_data.insert(key, value); + } + Ok(()) } - async fn delete_many( - &mut self, - bucket: Resource, - keys: Vec, - ) -> Result<(), Error> { + fn delete_many(&mut self, bucket: Resource, keys: Vec) -> Result<(), Error> { let bucket = self.table.get_mut(&bucket)?; - bucket.inner.delete_many(keys).await + for key in keys { + bucket.in_memory_data.remove(&key); + } + Ok(()) } } /// Add all the `wasi-keyvalue` world's interfaces to a [`wasmtime::component::Linker`]. -/// -/// This function will add the `async` variant of all interfaces into the -/// `Linker` provided. By `async` this means that this function is only -/// compatible with [`Config::async_support(true)`][wasmtime::Config::async_support]. -/// For embeddings with async support disabled see [`add_to_linker_sync`] instead. -pub fn add_to_linker_async( +pub fn add_to_linker( l: &mut wasmtime::component::Linker, f: impl Fn(&mut T) -> WasiKeyValue<'_> + Send + Sync + Copy + 'static, ) -> Result<()> { @@ -413,121 +295,3 @@ pub fn add_to_linker_async( keyvalue::batch::add_to_linker_get_host(l, f)?; Ok(()) } - -impl keyvalue_sync::store::Host for WasiKeyValue<'_> { - fn open(&mut self, identifier: String) -> Result, Error> { - in_tokio(async { keyvalue::store::Host::open(self, identifier).await }) - } - - fn convert_error(&mut self, err: Error) -> Result { - match err { - Error::NoSuchStore => Ok(keyvalue_sync::store::Error::NoSuchStore), - Error::AccessDenied => Ok(keyvalue_sync::store::Error::AccessDenied), - Error::Other(e) => Ok(keyvalue_sync::store::Error::Other(e)), - } - } -} - -impl keyvalue_sync::store::HostBucket for WasiKeyValue<'_> { - fn get(&mut self, bucket: Resource, key: String) -> Result>, Error> { - in_tokio(async { keyvalue::store::HostBucket::get(self, bucket, key).await }) - } - - fn set(&mut self, bucket: Resource, key: String, value: Vec) -> Result<(), Error> { - in_tokio(async { keyvalue::store::HostBucket::set(self, bucket, key, value).await }) - } - - fn delete(&mut self, bucket: Resource, key: String) -> Result<(), Error> { - in_tokio(async { keyvalue::store::HostBucket::delete(self, bucket, key).await }) - } - - fn exists(&mut self, bucket: Resource, key: String) -> Result { - in_tokio(async { keyvalue::store::HostBucket::exists(self, bucket, key).await }) - } - - fn list_keys( - &mut self, - bucket: Resource, - cursor: Option, - ) -> Result { - in_tokio(async { - let resp = keyvalue::store::HostBucket::list_keys(self, bucket, cursor).await?; - Ok(keyvalue_sync::store::KeyResponse { - keys: resp.keys, - cursor: resp.cursor, - }) - }) - } - - fn drop(&mut self, bucket: Resource) -> Result<()> { - keyvalue::store::HostBucket::drop(self, bucket) - } -} - -impl keyvalue_sync::atomics::Host for WasiKeyValue<'_> { - fn increment( - &mut self, - bucket: Resource, - key: String, - delta: u64, - ) -> Result { - in_tokio(async { keyvalue::atomics::Host::increment(self, bucket, key, delta).await }) - } -} - -impl keyvalue_sync::batch::Host for WasiKeyValue<'_> { - fn get_many( - &mut self, - bucket: Resource, - keys: Vec, - ) -> Result)>>, Error> { - in_tokio(async { keyvalue::batch::Host::get_many(self, bucket, keys).await }) - } - - fn set_many( - &mut self, - bucket: Resource, - key_values: Vec<(String, Vec)>, - ) -> Result<(), Error> { - in_tokio(async { keyvalue::batch::Host::set_many(self, bucket, key_values).await }) - } - - fn delete_many(&mut self, bucket: Resource, keys: Vec) -> Result<(), Error> { - in_tokio(async { keyvalue::batch::Host::delete_many(self, bucket, keys).await }) - } -} - -/// Add all the `wasi-keyvalue` world's interfaces to a [`wasmtime::component::Linker`]. -/// -/// This function will add the `sync` variant of all interfaces into the -/// `Linker` provided. For embeddings with async support see -/// [`add_to_linker_async`] instead. -pub fn add_to_linker_sync( - l: &mut wasmtime::component::Linker, - f: impl Fn(&mut T) -> WasiKeyValue<'_> + Send + Sync + Copy + 'static, -) -> Result<()> { - keyvalue_sync::store::add_to_linker_get_host(l, f)?; - keyvalue_sync::atomics::add_to_linker_get_host(l, f)?; - keyvalue_sync::batch::add_to_linker_get_host(l, f)?; - Ok(()) -} - -#[cfg(test)] -mod tests { - #[test] - #[cfg(feature = "redis")] - fn test_allow_redis_host() { - let ctx = super::WasiKeyValueCtx::builder() - .allow_redis_hosts(&["127.0.0.1:1234", "localhost", "/var/run/redis.sock"]) - .build(); - assert!(ctx.allow_redis_host(&super::Url::parse("redis://127.0.0.1:1234/db").unwrap())); - assert!(ctx.allow_redis_host(&super::Url::parse("redis://localhost").unwrap())); - assert!(!ctx.allow_redis_host(&super::Url::parse("redis://192.168.0.1").unwrap())); - assert!(ctx.allow_redis_host( - &super::Url::parse("redis+unix:///var/run/redis.sock?db=db").unwrap() - )); - assert!(!ctx.allow_redis_host( - &super::Url::parse("redis+unix:///var/local/redis.sock?db=db").unwrap() - )); - } -} diff --git a/crates/wasi-keyvalue/src/provider/inmemory.rs b/crates/wasi-keyvalue/src/provider/inmemory.rs deleted file mode 100644 index de9c612c4f48..000000000000 --- a/crates/wasi-keyvalue/src/provider/inmemory.rs +++ /dev/null @@ -1,94 +0,0 @@ -use crate::{bindings::wasi::keyvalue::store::KeyResponse, to_other_error, Error, Host}; -use async_trait::async_trait; -use std::collections::HashMap; -use std::sync::{Arc, Mutex}; - -#[derive(Default)] -pub(crate) struct InMemory { - store: Arc>>>, -} - -impl InMemory { - pub(crate) fn new(data: HashMap>) -> Self { - Self { - store: Arc::new(Mutex::new(data)), - } - } -} - -#[async_trait] -impl Host for InMemory { - async fn get(&mut self, key: String) -> Result>, Error> { - let store = self.store.lock().unwrap(); - Ok(store.get(&key).cloned()) - } - - async fn set(&mut self, key: String, value: Vec) -> Result<(), Error> { - let mut store = self.store.lock().unwrap(); - store.insert(key, value); - Ok(()) - } - - async fn delete(&mut self, key: String) -> Result<(), Error> { - let mut store = self.store.lock().unwrap(); - store.remove(&key); - Ok(()) - } - - async fn exists(&mut self, key: String) -> Result { - let store = self.store.lock().unwrap(); - Ok(store.contains_key(&key)) - } - - async fn list_keys(&mut self, cursor: Option) -> Result { - let store = self.store.lock().unwrap(); - let keys: Vec = store.keys().cloned().collect(); - let cursor = cursor.unwrap_or(0) as usize; - let keys_slice = &keys[cursor..]; - Ok(KeyResponse { - keys: keys_slice.to_vec(), - cursor: None, - }) - } - - async fn increment(&mut self, key: String, delta: u64) -> Result { - let mut store = self.store.lock().unwrap(); - let value = store - .entry(key.clone()) - .or_insert("0".to_string().into_bytes()); - let current_value = String::from_utf8(value.clone()) - .map_err(to_other_error)? - .parse::() - .map_err(to_other_error)?; - let new_value = current_value + delta; - *value = new_value.to_string().into_bytes(); - Ok(new_value) - } - - async fn get_many( - &mut self, - keys: Vec, - ) -> Result)>>, Error> { - let store = self.store.lock().unwrap(); - Ok(keys - .into_iter() - .map(|key| store.get(&key).map(|value| (key.clone(), value.clone()))) - .collect()) - } - - async fn set_many(&mut self, key_values: Vec<(String, Vec)>) -> Result<(), Error> { - let mut store = self.store.lock().unwrap(); - for (key, value) in key_values { - store.insert(key, value); - } - Ok(()) - } - - async fn delete_many(&mut self, keys: Vec) -> Result<(), Error> { - let mut store = self.store.lock().unwrap(); - for key in keys { - store.remove(&key); - } - Ok(()) - } -} diff --git a/crates/wasi-keyvalue/src/provider/mod.rs b/crates/wasi-keyvalue/src/provider/mod.rs deleted file mode 100644 index 1ea5bdb90c1b..000000000000 --- a/crates/wasi-keyvalue/src/provider/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub(crate) mod inmemory; -#[cfg(feature = "redis")] -pub(crate) mod redis; diff --git a/crates/wasi-keyvalue/src/provider/redis.rs b/crates/wasi-keyvalue/src/provider/redis.rs deleted file mode 100644 index d4cab37ea578..000000000000 --- a/crates/wasi-keyvalue/src/provider/redis.rs +++ /dev/null @@ -1,106 +0,0 @@ -use crate::{bindings::wasi::keyvalue::store::KeyResponse, Error, Host}; -use anyhow::Result; -use async_trait::async_trait; -use redis::{aio::MultiplexedConnection, AsyncCommands, RedisError}; -use std::time::Duration; - -struct Redis { - conn: MultiplexedConnection, -} - -impl From for Error { - fn from(err: RedisError) -> Self { - Self::Other(err.to_string()) - } -} - -pub(crate) async fn open( - identifier: String, - response_timeout: Option, - connection_timeout: Option, -) -> Result { - let client = redis::Client::open(identifier)?; - let conn = client - .get_multiplexed_async_connection_with_timeouts( - response_timeout.unwrap_or(Duration::MAX), - connection_timeout.unwrap_or(Duration::MAX), - ) - .await?; - Ok(Redis { conn }) -} - -#[async_trait] -impl Host for Redis { - async fn get(&mut self, key: String) -> Result>, Error> { - let v: Option> = self.conn.get(key).await?; - Ok(v) - } - - async fn set(&mut self, key: String, value: Vec) -> Result<(), Error> { - let _: () = self.conn.set(key, value).await?; - Ok(()) - } - - async fn delete(&mut self, key: String) -> Result<(), Error> { - let _: () = self.conn.del(key).await?; - Ok(()) - } - - async fn exists(&mut self, key: String) -> Result { - let exists: bool = self.conn.exists(key).await?; - Ok(exists) - } - - async fn list_keys(&mut self, cursor: Option) -> Result { - let cursor = cursor.unwrap_or(0); - let (new_cursor, keys): (u64, Vec) = redis::cmd("SCAN") - .arg(cursor) - .query_async(&mut self.conn) - .await?; - - Ok(KeyResponse { - keys, - cursor: if new_cursor == 0 { - None - } else { - Some(new_cursor) - }, - }) - } - - async fn increment(&mut self, key: String, delta: u64) -> Result { - let v: u64 = self.conn.incr(key, delta).await?; - Ok(v) - } - - async fn get_many( - &mut self, - keys: Vec, - ) -> Result)>>, Error> { - let values: Vec>> = self.conn.get(keys.clone()).await?; - - Ok(keys - .into_iter() - .zip(values.into_iter()) - .map(|(key, value)| value.map(|v| (key, v))) - .collect()) - } - - async fn set_many(&mut self, key_values: Vec<(String, Vec)>) -> Result<(), Error> { - let mut pipe = redis::pipe(); - for (key, value) in key_values { - pipe.set(key, value).ignore(); - } - let _: () = pipe.query_async(&mut self.conn).await?; - Ok(()) - } - - async fn delete_many(&mut self, keys: Vec) -> Result<(), Error> { - let mut pipe = redis::pipe(); - for key in keys { - pipe.del(key).ignore(); - } - let _: () = pipe.query_async(&mut self.conn).await?; - Ok(()) - } -} diff --git a/crates/wasi-keyvalue/tests/main.rs b/crates/wasi-keyvalue/tests/main.rs index fc7a0fb44ecb..f4cb5580bed1 100644 --- a/crates/wasi-keyvalue/tests/main.rs +++ b/crates/wasi-keyvalue/tests/main.rs @@ -32,7 +32,7 @@ async fn run_wasi(path: &str, ctx: Ctx) -> Result<()> { let mut linker = Linker::new(&engine); wasmtime_wasi::add_to_linker_async(&mut linker)?; - wasmtime_wasi_keyvalue::add_to_linker_async(&mut linker, |h: &mut Ctx| { + wasmtime_wasi_keyvalue::add_to_linker(&mut linker, |h: &mut Ctx| { WasiKeyValue::new(&h.wasi_keyvalue_ctx, &mut h.table) })?; @@ -59,10 +59,7 @@ async fn keyvalue_main() -> Result<()> { KEYVALUE_MAIN_COMPONENT, Ctx { table: ResourceTable::new(), - wasi_ctx: WasiCtxBuilder::new() - .inherit_stderr() - .env("IDENTIFIER", "") - .build(), + wasi_ctx: WasiCtxBuilder::new().inherit_stderr().build(), wasi_keyvalue_ctx: WasiKeyValueCtxBuilder::new() .in_memory_data([("atomics_key", "5")]) .build(), @@ -70,24 +67,3 @@ async fn keyvalue_main() -> Result<()> { ) .await } - -#[cfg(feature = "redis")] -#[tokio::test(flavor = "multi_thread")] -async fn keyvalue_redis() -> Result<()> { - run_wasi( - KEYVALUE_MAIN_COMPONENT, - Ctx { - table: ResourceTable::new(), - wasi_ctx: WasiCtxBuilder::new() - .inherit_stderr() - .env("IDENTIFIER", "redis://127.0.0.1/") - .build(), - wasi_keyvalue_ctx: WasiKeyValueCtxBuilder::new() - .allow_redis_hosts(&["127.0.0.1"]) - .redis_connection_timeout(std::time::Duration::from_secs(5)) - .redis_response_timeout(std::time::Duration::from_secs(5)) - .build(), - }, - ) - .await -} diff --git a/src/commands/run.rs b/src/commands/run.rs index 16d153da68a2..4aa504aed2fd 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -724,24 +724,9 @@ impl RunCommand { .iter() .map(|v| (v.key.clone(), v.value.clone())), ) - .allow_redis_hosts(&self.run.common.wasi.keyvalue_redis_host) - .redis_connection_timeout( - self.run - .common - .wasi - .keyvalue_redis_connection_timeout - .unwrap_or(std::time::Duration::MAX), - ) - .redis_response_timeout( - self.run - .common - .wasi - .keyvalue_redis_response_timeout - .unwrap_or(std::time::Duration::MAX), - ) .build(); - wasmtime_wasi_keyvalue::add_to_linker_sync(linker, |h| { + wasmtime_wasi_keyvalue::add_to_linker(linker, |h| { let preview2_ctx = h.preview2_ctx.as_mut().expect("wasip2 is not configured"); let preview2_ctx = diff --git a/src/commands/serve.rs b/src/commands/serve.rs index 46361995b161..6f80fc4b08ef 100644 --- a/src/commands/serve.rs +++ b/src/commands/serve.rs @@ -206,21 +206,6 @@ impl ServeCommand { .iter() .map(|v| (v.key.clone(), v.value.clone())), ) - .allow_redis_hosts(&self.run.common.wasi.keyvalue_redis_host) - .redis_connection_timeout( - self.run - .common - .wasi - .keyvalue_redis_connection_timeout - .unwrap_or(std::time::Duration::MAX), - ) - .redis_response_timeout( - self.run - .common - .wasi - .keyvalue_redis_response_timeout - .unwrap_or(std::time::Duration::MAX), - ) .build(); host.wasi_keyvalue.replace(ctx); } @@ -309,7 +294,7 @@ impl ServeCommand { } #[cfg(feature = "wasi-keyvalue")] { - wasmtime_wasi_keyvalue::add_to_linker_async(linker, |h: &mut Host| { + wasmtime_wasi_keyvalue::add_to_linker(linker, |h: &mut Host| { WasiKeyValue::new(h.wasi_keyvalue.as_ref().unwrap(), &mut h.table) })?; }