Repository: bytecodealliance/wasmtime
Pull request #9062 (2024-08-02 09:07:26)

wasi-keyvalue: remove the Redis provider
Previously discussed here:  https://github.com/bytecodealliance/wasmtime/pull/8983#issuecomment-2264813319

Since the specific form of the ["host plugins"](https://github.com/bytecodealliance/wasmtime/pull/8983#issuecomment-2261639047) is still unclear, this PR reverts the implementation to its original state, considering only the in-memory provider. It does not retain any extension interfaces. I believe we can only make the relevant changes after we have decided how to implement those host plugins.

cc @alexcrichton @thomastaylor312 @fitzgen 


Patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index c799f98f2..ada3daffb 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 ff405a4fa..6cbf31231 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 8a511b20e..1db20a531 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/crates/cli-flags/src/lib.rs b/crates/cli-flags/src/lib.rs
index 6f65e6de0..f172d2541 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<KeyValuePair>,
         /// Preset data for the In-Memory provider of WASI key-value API.
         pub keyvalue_in_memory_data: Vec<KeyValuePair>,
-        /// Grant access to the given Redis host for the Redis provider of WASI
-        /// key-value API.
-        pub keyvalue_redis_host: Vec<String>,
-        /// Sets the connection timeout parameter for the Redis provider of WASI
-        /// key-value API.
-        pub keyvalue_redis_connection_timeout: Option<Duration>,
-        /// Sets the response timeout parameter for the Redis provider of WASI
-        /// key-value API.
-        pub keyvalue_redis_response_timeout: Option<Duration>,
     }
 
     enum Wasi {
diff --git a/crates/wasi-keyvalue/Cargo.toml b/crates/wasi-keyvalue/Cargo.toml
index 7802af6bf..561f33bb1 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", "std"] }
 
 [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 56091c238..000000000
--- 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 46addae25..c45e501f8 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://[<username>][:<password>@]<hostname>[:port][/<db>]`
-//!   * `redis+unix:///<path>[?db=<db>[&pass=<password>][&user=<username>]]`
 //!
 //! # 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::<Ctx>::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<ResourceTableError> for Error {
     }
 }
 
-pub(crate) fn to_other_error(e: impl Display) -> Error {
-    Error::Other(e.to_string())
-}
-
 #[doc(hidden)]
 pub struct Bucket {
-    inner: Box<dyn Host + Send>,
-}
-
-#[async_trait]
-trait Host {
-    async fn get(&mut self, key: String) -> Result<Option<Vec<u8>>, Error>;
-
-    async fn set(&mut self, key: String, value: Vec<u8>) -> Result<(), Error>;
-
-    async fn delete(&mut self, key: String) -> Result<(), Error>;
-
-    async fn exists(&mut self, key: String) -> Result<bool, Error>;
-
-    async fn list_keys(
-        &mut self,
-        cursor: Option<u64>,
-    ) -> Result<keyvalue::store::KeyResponse, Error>;
-
-    async fn increment(&mut self, key: String, delta: u64) -> Result<u64, Error>;
-
-    async fn get_many(
-        &mut self,
-        keys: Vec<String>,
-    ) -> Result<Vec<Option<(String, Vec<u8>)>>, Error>;
-
-    async fn set_many(&mut self, key_values: Vec<(String, Vec<u8>)>) -> Result<(), Error>;
-
-    async fn delete_many(&mut self, keys: Vec<String>) -> Result<(), Error>;
+    in_memory_data: HashMap<String, Vec<u8>>,
 }
 
 /// Builder-style structure used to create a [`WasiKeyValueCtx`].
 #[derive(Default)]
 pub struct WasiKeyValueCtxBuilder {
     in_memory_data: HashMap<String, Vec<u8>>,
-    #[cfg(feature = "redis")]
-    allowed_redis_hosts: Vec<String>,
-    #[cfg(feature = "redis")]
-    redis_connection_timeout: Option<std::time::Duration>,
-    #[cfg(feature = "redis")]
-    redis_response_timeout: Option<std::time::Duration>,
 }
 
 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 `<hostname>[: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<str>]) -> 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<String, Vec<u8>>,
-    #[cfg(feature = "redis")]
-    allowed_redis_hosts: Vec<String>,
-    #[cfg(feature = "redis")]
-    redis_connection_timeout: Option<std::time::Duration>,
-    #[cfg(feature = "redis")]
-    redis_response_timeout: Option<std::time::Duration>,
 }
 
 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<Resource<Bucket>, 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<Resource<Bucket>, 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<Bucket>,
-        key: String,
-    ) -> Result<Option<Vec<u8>>, Error> {
+    fn get(&mut self, bucket: Resource<Bucket>, key: String) -> Result<Option<Vec<u8>>, 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<Bucket>,
-        key: String,
-        value: Vec<u8>,
-    ) -> Result<(), Error> {
+    fn set(&mut self, bucket: Resource<Bucket>, key: String, value: Vec<u8>) -> 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<Bucket>, key: String) -> Result<(), Error> {
+    fn delete(&mut self, bucket: Resource<Bucket>, 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<Bucket>, key: String) -> Result<bool, Error> {
+    fn exists(&mut self, bucket: Resource<Bucket>, key: String) -> Result<bool, Error> {
         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<Bucket>,
         cursor: Option<u64>,
     ) -> Result<keyvalue::store::KeyResponse, Error> {
         let bucket = self.table.get_mut(&bucket)?;
-        bucket.inner.list_keys(cursor).await
+        let keys: Vec<String> = 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<Bucket>) -> 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<Bucket>,
         key: String,
         delta: u64,
     ) -> Result<u64, Error> {
         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::<u64>()
+            .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<Bucket>,
         keys: Vec<String>,
     ) -> Result<Vec<Option<(String, Vec<u8>)>>, 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<Bucket>,
         key_values: Vec<(String, Vec<u8>)>,
     ) -> 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<Bucket>,
-        keys: Vec<String>,
-    ) -> Result<(), Error> {
+    fn delete_many(&mut self, bucket: Resource<Bucket>, keys: Vec<String>) -> 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<T: Send>(
+pub fn add_to_linker<T: Send>(
     l: &mut wasmtime::component::Linker<T>,
     f: impl Fn(&mut T) -> WasiKeyValue<'_> + Send + Sync + Copy + 'static,
 ) -> Result<()> {
@@ -413,121 +295,3 @@ pub fn add_to_linker_async<T: Send>(
     keyvalue::batch::add_to_linker_get_host(l, f)?;
     Ok(())
 }
-
-impl keyvalue_sync::store::Host for WasiKeyValue<'_> {
-    fn open(&mut self, identifier: String) -> Result<Resource<Bucket>, Error> {
-        in_tokio(async { keyvalue::store::Host::open(self, identifier).await })
-    }
-
-    fn convert_error(&mut self, err: Error) -> Result<keyvalue_sync::store::Error> {
-        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<Bucket>, key: String) -> Result<Option<Vec<u8>>, Error> {
-        in_tokio(async { keyvalue::store::HostBucket::get(self, bucket, key).await })
-    }
-
-    fn set(&mut self, bucket: Resource<Bucket>, key: String, value: Vec<u8>) -> Result<(), Error> {
-        in_tokio(async { keyvalue::store::HostBucket::set(self, bucket, key, value).await })
-    }
-
-    fn delete(&mut self, bucket: Resource<Bucket>, key: String) -> Result<(), Error> {
-        in_tokio(async { keyvalue::store::HostBucket::delete(self, bucket, key).await })
-    }
-
-    fn exists(&mut self, bucket: Resource<Bucket>, key: String) -> Result<bool, Error> {
-        in_tokio(async { keyvalue::store::HostBucket::exists(self, bucket, key).await })
-    }
-
-    fn list_keys(
-        &mut self,
-        bucket: Resource<Bucket>,
-        cursor: Option<u64>,
-    ) -> Result<keyvalue_sync::store::KeyResponse, Error> {
-        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<Bucket>) -> Result<()> {
-        keyvalue::store::HostBucket::drop(self, bucket)
-    }
-}
-
-impl keyvalue_sync::atomics::Host for WasiKeyValue<'_> {
-    fn increment(
-        &mut self,
-        bucket: Resource<Bucket>,
-        key: String,
-        delta: u64,
-    ) -> Result<u64, Error> {
-        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<Bucket>,
-        keys: Vec<String>,
-    ) -> Result<Vec<Option<(String, Vec<u8>)>>, Error> {
-        in_tokio(async { keyvalue::batch::Host::get_many(self, bucket, keys).await })
-    }
-
-    fn set_many(
-        &mut self,
-        bucket: Resource<Bucket>,
-        key_values: Vec<(String, Vec<u8>)>,
-    ) -> Result<(), Error> {
-        in_tokio(async { keyvalue::batch::Host::set_many(self, bucket, key_values).await })
-    }
-
-    fn delete_many(&mut self, bucket: Resource<Bucket>, keys: Vec<String>) -> 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<T>(
-    l: &mut wasmtime::component::Linker<T>,
-    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 de9c612c4..000000000
--- 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<Mutex<HashMap<String, Vec<u8>>>>,
-}
-
-impl InMemory {
-    pub(crate) fn new(data: HashMap<String, Vec<u8>>) -> Self {
-        Self {
-            store: Arc::new(Mutex::new(data)),
-        }
-    }
-}
-
-#[async_trait]
-impl Host for InMemory {
-    async fn get(&mut self, key: String) -> Result<Option<Vec<u8>>, Error> {
-        let store = self.store.lock().unwrap();
-        Ok(store.get(&key).cloned())
-    }
-
-    async fn set(&mut self, key: String, value: Vec<u8>) -> 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<bool, Error> {
-        let store = self.store.lock().unwrap();
-        Ok(store.contains_key(&key))
-    }
-
-    async fn list_keys(&mut self, cursor: Option<u64>) -> Result<KeyResponse, Error> {
-        let store = self.store.lock().unwrap();
-        let keys: Vec<String> = 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<u64, Error> {
-        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::<u64>()
-            .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<String>,
-    ) -> Result<Vec<Option<(String, Vec<u8>)>>, 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<u8>)>) -> 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<String>) -> 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 1ea5bdb90..000000000
--- 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 d4cab37ea..000000000
--- 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<RedisError> for Error {
-    fn from(err: RedisError) -> Self {
-        Self::Other(err.to_string())
-    }
-}
-
-pub(crate) async fn open(
-    identifier: String,
-    response_timeout: Option<Duration>,
-    connection_timeout: Option<Duration>,
-) -> Result<impl Host, RedisError> {
-    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<Option<Vec<u8>>, Error> {
-        let v: Option<Vec<u8>> = self.conn.get(key).await?;
-        Ok(v)
-    }
-
-    async fn set(&mut self, key: String, value: Vec<u8>) -> 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<bool, Error> {
-        let exists: bool = self.conn.exists(key).await?;
-        Ok(exists)
-    }
-
-    async fn list_keys(&mut self, cursor: Option<u64>) -> Result<KeyResponse, Error> {
-        let cursor = cursor.unwrap_or(0);
-        let (new_cursor, keys): (u64, Vec<String>) = 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<u64, Error> {
-        let v: u64 = self.conn.incr(key, delta).await?;
-        Ok(v)
-    }
-
-    async fn get_many(
-        &mut self,
-        keys: Vec<String>,
-    ) -> Result<Vec<Option<(String, Vec<u8>)>>, Error> {
-        let values: Vec<Option<Vec<u8>>> = 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<u8>)>) -> 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<String>) -> 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/src/commands/run.rs b/src/commands/run.rs
index 16d153da6..4aa504aed 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 46361995b..6f80fc4b0 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)
                 })?;
             }
diff --git a/supply-chain/config.toml b/supply-chain/config.toml
index 61c5a1946..fad902ac8 100644
--- a/supply-chain/config.toml
+++ b/supply-chain/config.toml
@@ -213,10 +213,6 @@ criteria = "safe-to-deploy"
 version = "0.16.0"
 criteria = "safe-to-deploy"
 
-[[exemptions.combine]]
-version = "4.6.7"
-criteria = "safe-to-deploy"
-
 [[exemptions.console]]
 version = "0.15.0"
 criteria = "safe-to-deploy"
@@ -431,10 +427,6 @@ criteria = "safe-to-deploy"
 version = "0.3.0"
 criteria = "safe-to-deploy"
 
-[[exemptions.redis]]
-version = "0.25.4"
-criteria = "safe-to-deploy"
-
 [[exemptions.redox_syscall]]
 version = "0.2.13"
 criteria = "safe-to-deploy"


Test patch:
diff --git a/ci/run-tests.sh b/ci/run-tests.sh
index 1822a3fd0..274d35232 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/test-programs/src/bin/keyvalue_main.rs b/crates/test-programs/src/bin/keyvalue_main.rs
index 486f59e19..5fc884a82 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/tests/main.rs b/crates/wasi-keyvalue/tests/main.rs
index fc7a0fb44..f4cb5580b 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
-}