Skip to content

Commit

Permalink
Add SqliteDefaultStoreSummaryHook
Browse files Browse the repository at this point in the history
Signed-off-by: Lann Martin <[email protected]>
  • Loading branch information
lann committed Aug 29, 2024
1 parent 1e3def5 commit 99547a9
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 12 deletions.
8 changes: 8 additions & 0 deletions crates/factor-key-value/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,17 @@ pub struct AppState {
}

impl AppState {
/// Returns the [`StoreManager::summary`] for the given store label.
pub fn store_summary(&self, label: &str) -> Option<String> {
self.store_manager.summary(label)
}

/// Returns true if the given store label is used by any component.
pub fn store_is_used(&self, label: &str) -> bool {
self.component_allowed_stores
.values()
.any(|stores| stores.contains(label))
}
}

pub struct InstanceBuilder {
Expand Down
19 changes: 15 additions & 4 deletions crates/factor-sqlite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,17 @@ impl AppState {
/// Get a connection for a given database label.
///
/// Returns `None` if there is no connection creator for the given label.
pub async fn get_connection(
&self,
label: &str,
) -> Option<Result<Box<dyn Connection>, v2::Error>> {
pub fn get_connection(&self, label: &str) -> Option<Result<Box<dyn Connection>, v2::Error>> {
let connection = (self.get_connection_creator)(label)?.create_connection();
Some(connection)
}

/// Returns true if the given database label is used by any component.
pub fn database_is_used(&self, label: &str) -> bool {
self.allowed_databases
.values()
.any(|stores| stores.contains(label))
}
}

/// A creator of a connections for a particular SQLite database.
Expand Down Expand Up @@ -205,4 +209,11 @@ pub trait Connection: Send + Sync {
) -> Result<v2::QueryResult, v2::Error>;

async fn execute_batch(&self, statements: &str) -> anyhow::Result<()>;

/// A human-readable summary of the connection's configuration
///
/// Example: "libSQL at libsql://example.com"
fn summary(&self) -> Option<String> {
None
}
}
7 changes: 7 additions & 0 deletions crates/sqlite-inproc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,13 @@ impl Connection for InProcConnection {
async fn execute_batch(&self, statements: &str) -> anyhow::Result<()> {
self.execute_batch(statements).await
}

fn summary(&self) -> Option<String> {
Some(match &self.location {
InProcDatabaseLocation::InMemory => "a temporary in-memory database".to_string(),
InProcDatabaseLocation::Path(path) => format!("\"{}\"", path.display()),
})
}
}

fn execute_query(
Expand Down
4 changes: 4 additions & 0 deletions crates/sqlite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ impl Connection for LibSqlConnection {
let client = self.get_client().await?;
client.execute_batch(statements).await
}

fn summary(&self) -> Option<String> {
Some(format!("libSQL at {}", self.url))
}
}

/// Configuration for a local SQLite database.
Expand Down
4 changes: 2 additions & 2 deletions crates/trigger/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use spin_core::async_trait;
use spin_factors_executor::{ComponentLoader, FactorsExecutor};
use spin_runtime_config::{ResolvedRuntimeConfig, UserProvidedPath};
use sqlite_statements::SqlStatementExecutorHook;
use summary::KeyValueDefaultStoreSummaryHook;
use summary::{KeyValueDefaultStoreSummaryHook, SqliteDefaultStoreSummaryHook};

use crate::factors::{TriggerFactors, TriggerFactorsRuntimeConfig};
use crate::stdio::{FollowComponents, StdioLoggingExecutorHooks};
Expand Down Expand Up @@ -424,8 +424,8 @@ impl<T: Trigger> TriggerAppBuilder<T> {
// TODO:
// builder.hooks(SummariseRuntimeConfigHook::new(&self.runtime_config_file));
executor.add_hooks(KeyValueDefaultStoreSummaryHook);
executor.add_hooks(SqliteDefaultStoreSummaryHook);
executor.add_hooks(SqlStatementExecutorHook::new(options.sqlite_statements));
// builder.hooks(SqlitePersistenceMessageHook);

let configured_app = {
let _sloth_guard = warn_if_wasm_build_slothful();
Expand Down
1 change: 0 additions & 1 deletion crates/trigger/src/cli/sqlite_statements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ impl SqlStatementExecutorHook {
let get_database = |label| async move {
sqlite
.get_connection(label)
.await
.transpose()
.with_context(|| format!("failed connect to database with label '{label}'"))
};
Expand Down
42 changes: 37 additions & 5 deletions crates/trigger/src/cli/summary.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use spin_core::async_trait;
use spin_factor_key_value::KeyValueFactor;
use spin_factor_sqlite::SqliteFactor;
use spin_factors_executor::ExecutorHooks;

use crate::factors::TriggerFactors;

/// An [`ExecutorHooks`] that prints information about the default KV store.
pub struct KeyValueDefaultStoreSummaryHook;

#[async_trait]
Expand All @@ -12,13 +14,43 @@ impl<U> ExecutorHooks<TriggerFactors, U> for KeyValueDefaultStoreSummaryHook {
&mut self,
configured_app: &spin_factors::ConfiguredApp<TriggerFactors>,
) -> anyhow::Result<()> {
if let Some(default_store_summary) = configured_app
.app_state::<KeyValueFactor>()
.ok()
.and_then(|kv_state| kv_state.store_summary("default"))
{
let Ok(kv_app_state) = configured_app.app_state::<KeyValueFactor>() else {
return Ok(());
};
if !kv_app_state.store_is_used("default") {
// We don't talk about unused default stores
return Ok(());
}
if let Some(default_store_summary) = kv_app_state.store_summary("default") {
println!("Storing default key-value data to {default_store_summary}.");
}
Ok(())
}
}

/// An [`ExecutorHooks`] that prints information about the default KV store.
pub struct SqliteDefaultStoreSummaryHook;

#[async_trait]
impl<U> ExecutorHooks<TriggerFactors, U> for SqliteDefaultStoreSummaryHook {
async fn configure_app(
&mut self,
configured_app: &spin_factors::ConfiguredApp<TriggerFactors>,
) -> anyhow::Result<()> {
let Ok(sqlite_app_state) = configured_app.app_state::<SqliteFactor>() else {
return Ok(());
};
if !sqlite_app_state.database_is_used("default") {
// We don't talk about unused default databases
return Ok(());
}
if let Some(default_database_summary) = sqlite_app_state
.get_connection("default")
.and_then(Result::ok)
.and_then(|conn| conn.summary())
{
println!("Storing default SQLite data to {default_database_summary}.");
}
Ok(())
}
}

0 comments on commit 99547a9

Please sign in to comment.