Skip to content

Commit

Permalink
Merge pull request #1408 from fermyon/sqlite-host-component
Browse files Browse the repository at this point in the history
[SIP] sqlite host component
  • Loading branch information
rylev authored May 22, 2023
2 parents 734cba8 + c515559 commit 276b5cf
Show file tree
Hide file tree
Showing 26 changed files with 843 additions and 13 deletions.
27 changes: 22 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,10 @@ tracing = { version = "0.1", features = ["log"] }
wasmtime-wasi = { version = "8.0.1", features = ["tokio"] }
wasi-common-preview1 = { package = "wasi-common", version = "8.0.1" }
wasmtime = { version = "8.0.1", features = ["component-model"] }
spin-componentize = { git = "https://github.com/fermyon/spin-componentize", rev = "51c3fade751c4e364142719e42130943fd8b0a76" }
wasi-host = { package = "host", git = "https://github.com/fermyon/spin-componentize", rev = "51c3fade751c4e364142719e42130943fd8b0a76" }
wasi-common = { git = "https://github.com/fermyon/spin-componentize", rev = "51c3fade751c4e364142719e42130943fd8b0a76" }
wasi-cap-std-sync = { git = "https://github.com/fermyon/spin-componentize", rev = "51c3fade751c4e364142719e42130943fd8b0a76" }
spin-componentize = { git = "https://github.com/fermyon/spin-componentize", rev = "b6d42fe41e5690844a661deb631d996a2b49debc" }
wasi-host = { package = "host", git = "https://github.com/fermyon/spin-componentize", rev = "b6d42fe41e5690844a661deb631d996a2b49debc" }
wasi-common = { git = "https://github.com/fermyon/spin-componentize", rev = "b6d42fe41e5690844a661deb631d996a2b49debc" }
wasi-cap-std-sync = { git = "https://github.com/fermyon/spin-componentize", rev = "b6d42fe41e5690844a661deb631d996a2b49debc" }

[workspace.dependencies.bindle]
git = "https://github.com/fermyon/bindle"
Expand Down
1 change: 1 addition & 0 deletions crates/bindle/src/expander.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ async fn bindle_component_manifest(
files: asset_group,
allowed_http_hosts: local.wasm.allowed_http_hosts.clone(),
key_value_stores: local.wasm.key_value_stores.clone(),
sqlite_databases: local.wasm.sqlite_databases.clone(),
},
trigger: local.trigger.clone(),
config: local.config.clone(),
Expand Down
2 changes: 1 addition & 1 deletion crates/key-value/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ doctest = false

[dependencies]
anyhow = "1.0"
tokio = { version = "1", features = [ "macros" ] }
tokio = { version = "1", features = [ "macros", "sync" ] }
spin-app = { path = "../app" }
spin-core = { path = "../core" }
spin-world = { path = "../world" }
Expand Down
2 changes: 1 addition & 1 deletion crates/key-value/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{collections::HashSet, sync::Arc};
use table::Table;

mod host_component;
mod table;
pub mod table;
mod util;

pub use host_component::{manager, KeyValueComponent};
Expand Down
1 change: 1 addition & 0 deletions crates/key-value/src/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ impl<V> Table<V> {
///
/// This function will attempt to avoid reusing recently closed identifiers, but after 2^32 calls to this
/// function they will start repeating.
#[allow(clippy::result_unit_err)]
pub fn push(&mut self, value: V) -> Result<u32, ()> {
if self.tuples.len() == self.capacity as usize {
Err(())
Expand Down
2 changes: 2 additions & 0 deletions crates/loader/src/bindle/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ pub struct RawWasmConfig {
pub allowed_http_hosts: Option<Vec<String>>,
/// Optional list of key-value stores the component is allowed to use.
pub key_value_stores: Option<Vec<String>>,
/// Optional list of SQLite databases the component is allowed to use.
pub sqlite_databases: Option<Vec<String>>,
/// Environment variables to be mapped inside the Wasm module at runtime.
pub environment: Option<HashMap<String, String>>,
}
2 changes: 2 additions & 0 deletions crates/loader/src/bindle/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,13 @@ async fn core(
let environment = raw.wasm.environment.unwrap_or_default();
let allowed_http_hosts = raw.wasm.allowed_http_hosts.unwrap_or_default();
let key_value_stores = raw.wasm.key_value_stores.unwrap_or_default();
let sqlite_databases = raw.wasm.sqlite_databases.unwrap_or_default();
let wasm = WasmConfig {
environment,
mounts,
allowed_http_hosts,
key_value_stores,
sqlite_databases,
};
let config = raw.config.unwrap_or_default();
Ok(CoreComponent {
Expand Down
2 changes: 2 additions & 0 deletions crates/loader/src/local/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ pub struct RawWasmConfig {
pub allowed_http_hosts: Option<Vec<String>>,
/// Optional list of key-value stores the component is allowed to use.
pub key_value_stores: Option<Vec<String>>,
/// Optional list of sqlite databases the component is allowed to use.
pub sqlite_databases: Option<Vec<String>>,
/// Environment variables to be mapped inside the Wasm module at runtime.
pub environment: Option<HashMap<String, String>>,
}
Expand Down
2 changes: 2 additions & 0 deletions crates/loader/src/local/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,13 @@ async fn core(
let environment = raw.wasm.environment.unwrap_or_default();
let allowed_http_hosts = raw.wasm.allowed_http_hosts.unwrap_or_default();
let key_value_stores = raw.wasm.key_value_stores.unwrap_or_default();
let sqlite_databases = raw.wasm.sqlite_databases.unwrap_or_default();
let wasm = WasmConfig {
environment,
mounts,
allowed_http_hosts,
key_value_stores,
sqlite_databases,
};
let config = raw.config.unwrap_or_default();
Ok(CoreComponent {
Expand Down
2 changes: 2 additions & 0 deletions crates/manifest/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ pub struct WasmConfig {
pub allowed_http_hosts: Vec<String>,
/// Optional list of key-value stores the component is allowed to use.
pub key_value_stores: Vec<String>,
/// Optional list of sqlite databases the component is allowed to use.
pub sqlite_databases: Vec<String>,
}

/// Directory mount for the assets of a component.
Expand Down
16 changes: 16 additions & 0 deletions crates/sqlite/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "spin-sqlite"
version = "0.1.0"
edition = "2021"

[dependencies]
spin-core = { path = "../core" }
spin-app = { path = "../app" }
spin-key-value = { path = "../key-value" }
spin-world = { path = "../world" }
anyhow = "1.0"
wit-bindgen-wasmtime = { workspace = true }
rusqlite = { version = "0.29.0", features = [ "bundled" ] }
rand = "0.8"
once_cell = "1"
tokio = "1"
93 changes: 93 additions & 0 deletions crates/sqlite/src/host_component.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use std::{
collections::HashMap,
path::PathBuf,
sync::{Arc, Mutex},
};

use once_cell::sync::OnceCell;
use rusqlite::Connection;
use spin_app::{AppComponent, DynamicHostComponent};
use spin_core::HostComponent;
use spin_world::sqlite;

use crate::SqliteImpl;

#[derive(Debug, Clone)]
pub enum DatabaseLocation {
InMemory,
Path(PathBuf),
}

/// A connection to a sqlite database
pub struct SqliteConnection {
location: DatabaseLocation,
connection: OnceCell<Arc<Mutex<Connection>>>,
}

impl SqliteConnection {
pub fn new(location: DatabaseLocation) -> Self {
Self {
location,
connection: OnceCell::new(),
}
}
}

impl ConnectionManager for SqliteConnection {
fn get_connection(&self) -> Result<Arc<Mutex<Connection>>, sqlite::Error> {
let connection = self
.connection
.get_or_try_init(|| -> Result<_, sqlite::Error> {
let c = match &self.location {
DatabaseLocation::InMemory => Connection::open_in_memory(),
DatabaseLocation::Path(path) => Connection::open(path),
}
.map_err(|e| sqlite::Error::Io(e.to_string()))?;
Ok(Arc::new(Mutex::new(c)))
})?
.clone();
Ok(connection)
}
}

pub trait ConnectionManager: Send + Sync {
fn get_connection(&self) -> Result<Arc<Mutex<Connection>>, sqlite::Error>;
}

pub struct SqliteComponent {
connection_managers: HashMap<String, Arc<dyn ConnectionManager>>,
}

impl SqliteComponent {
pub fn new(connection_managers: HashMap<String, Arc<dyn ConnectionManager>>) -> Self {
Self {
connection_managers,
}
}
}

impl HostComponent for SqliteComponent {
type Data = super::SqliteImpl;

fn add_to_linker<T: Send>(
linker: &mut spin_core::Linker<T>,
get: impl Fn(&mut spin_core::Data<T>) -> &mut Self::Data + Send + Sync + Copy + 'static,
) -> anyhow::Result<()> {
sqlite::add_to_linker(linker, get)
}

fn build_data(&self) -> Self::Data {
SqliteImpl::new(self.connection_managers.clone())
}
}

impl DynamicHostComponent for SqliteComponent {
fn update_data(&self, data: &mut Self::Data, component: &AppComponent) -> anyhow::Result<()> {
let allowed_databases = component
.get_metadata(crate::DATABASES_KEY)?
.unwrap_or_default();
data.component_init(allowed_databases);
// TODO: allow dynamically updating connection manager
Ok(())
}
}
Loading

0 comments on commit 276b5cf

Please sign in to comment.