Skip to content

Commit fe39e66

Browse files
authored
Merge pull request #2786 from lann/sqlite-summary-hook
Add SqliteDefaultStoreSummaryHook
2 parents 2cd5826 + 99547a9 commit fe39e66

File tree

10 files changed

+77
-24
lines changed

10 files changed

+77
-24
lines changed

crates/factor-key-value/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,17 @@ pub struct AppState {
120120
}
121121

122122
impl AppState {
123+
/// Returns the [`StoreManager::summary`] for the given store label.
123124
pub fn store_summary(&self, label: &str) -> Option<String> {
124125
self.store_manager.summary(label)
125126
}
127+
128+
/// Returns true if the given store label is used by any component.
129+
pub fn store_is_used(&self, label: &str) -> bool {
130+
self.component_allowed_stores
131+
.values()
132+
.any(|stores| stores.contains(label))
133+
}
126134
}
127135

128136
pub struct InstanceBuilder {

crates/factor-sqlite/src/lib.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,13 +169,17 @@ impl AppState {
169169
/// Get a connection for a given database label.
170170
///
171171
/// Returns `None` if there is no connection creator for the given label.
172-
pub async fn get_connection(
173-
&self,
174-
label: &str,
175-
) -> Option<Result<Box<dyn Connection>, v2::Error>> {
172+
pub fn get_connection(&self, label: &str) -> Option<Result<Box<dyn Connection>, v2::Error>> {
176173
let connection = (self.get_connection_creator)(label)?.create_connection();
177174
Some(connection)
178175
}
176+
177+
/// Returns true if the given database label is used by any component.
178+
pub fn database_is_used(&self, label: &str) -> bool {
179+
self.allowed_databases
180+
.values()
181+
.any(|stores| stores.contains(label))
182+
}
179183
}
180184

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

207211
async fn execute_batch(&self, statements: &str) -> anyhow::Result<()>;
212+
213+
/// A human-readable summary of the connection's configuration
214+
///
215+
/// Example: "libSQL at libsql://example.com"
216+
fn summary(&self) -> Option<String> {
217+
None
218+
}
208219
}

crates/factors-executor/src/lib.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,7 @@ impl<T: RuntimeFactors, U: Send + 'static> FactorsExecutor<T, U> {
3434
}
3535
}
3636

37-
impl<T: RuntimeFactors, U: Send + 'static> FactorsExecutor<T, U>
38-
where
39-
T::AppState: Sync,
40-
{
37+
impl<T: RuntimeFactors, U: Send + 'static> FactorsExecutor<T, U> {
4138
/// Adds the given [`ExecutorHooks`] to this executor.
4239
///
4340
/// Hooks are run in the order they are added.
@@ -84,7 +81,6 @@ where
8481
pub trait ExecutorHooks<T, U>: Send + Sync
8582
where
8683
T: RuntimeFactors,
87-
T::AppState: Sync,
8884
{
8985
/// Configure app hooks run immediately after [`RuntimeFactors::configure_app`].
9086
async fn configure_app(&mut self, configured_app: &ConfiguredApp<T>) -> anyhow::Result<()> {
@@ -143,10 +139,7 @@ impl<T: RuntimeFactors, U: Send + 'static> FactorsExecutorApp<T, U> {
143139
}
144140
}
145141

146-
impl<T: RuntimeFactors, U: Send + 'static> FactorsExecutorApp<T, U>
147-
where
148-
T::AppState: Sync,
149-
{
142+
impl<T: RuntimeFactors, U: Send + 'static> FactorsExecutorApp<T, U> {
150143
/// Returns an instance builder for the given component ID.
151144
pub fn prepare(&self, component_id: &str) -> anyhow::Result<FactorsInstanceBuilder<T, U>> {
152145
let app_component = self

crates/factors/src/factor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub trait Factor: Any + Sized {
1717
/// The application state of this factor.
1818
///
1919
/// This state *may* be cached by the runtime across multiple requests.
20-
type AppState;
20+
type AppState: Sync;
2121

2222
/// The builder of instance state for this factor.
2323
type InstanceBuilder: FactorInstanceBuilder;

crates/factors/src/runtime_factors.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use crate::{factor::FactorInstanceState, App, ConfiguredApp, Factor};
3232
/// ```
3333
pub trait RuntimeFactors: Sized + 'static {
3434
/// The per application state of all the factors.
35-
type AppState;
35+
type AppState: Sync;
3636
/// The per instance state of the factors.
3737
type InstanceState: RuntimeFactorsInstanceState;
3838
/// The collection of all the `InstanceBuilder`s of the factors.

crates/sqlite-inproc/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,13 @@ impl Connection for InProcConnection {
112112
async fn execute_batch(&self, statements: &str) -> anyhow::Result<()> {
113113
self.execute_batch(statements).await
114114
}
115+
116+
fn summary(&self) -> Option<String> {
117+
Some(match &self.location {
118+
InProcDatabaseLocation::InMemory => "a temporary in-memory database".to_string(),
119+
InProcDatabaseLocation::Path(path) => format!("\"{}\"", path.display()),
120+
})
121+
}
115122
}
116123

117124
fn execute_query(

crates/sqlite/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@ impl Connection for LibSqlConnection {
166166
let client = self.get_client().await?;
167167
client.execute_batch(statements).await
168168
}
169+
170+
fn summary(&self) -> Option<String> {
171+
Some(format!("libSQL at {}", self.url))
172+
}
169173
}
170174

171175
/// Configuration for a local SQLite database.

crates/trigger/src/cli.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use spin_core::async_trait;
1515
use spin_factors_executor::{ComponentLoader, FactorsExecutor};
1616
use spin_runtime_config::{ResolvedRuntimeConfig, UserProvidedPath};
1717
use sqlite_statements::SqlStatementExecutorHook;
18-
use summary::KeyValueDefaultStoreSummaryHook;
18+
use summary::{KeyValueDefaultStoreSummaryHook, SqliteDefaultStoreSummaryHook};
1919

2020
use crate::factors::{TriggerFactors, TriggerFactorsRuntimeConfig};
2121
use crate::stdio::{FollowComponents, StdioLoggingExecutorHooks};
@@ -424,8 +424,8 @@ impl<T: Trigger> TriggerAppBuilder<T> {
424424
// TODO:
425425
// builder.hooks(SummariseRuntimeConfigHook::new(&self.runtime_config_file));
426426
executor.add_hooks(KeyValueDefaultStoreSummaryHook);
427+
executor.add_hooks(SqliteDefaultStoreSummaryHook);
427428
executor.add_hooks(SqlStatementExecutorHook::new(options.sqlite_statements));
428-
// builder.hooks(SqlitePersistenceMessageHook);
429429

430430
let configured_app = {
431431
let _sloth_guard = warn_if_wasm_build_slothful();

crates/trigger/src/cli/sqlite_statements.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ impl SqlStatementExecutorHook {
3131
let get_database = |label| async move {
3232
sqlite
3333
.get_connection(label)
34-
.await
3534
.transpose()
3635
.with_context(|| format!("failed connect to database with label '{label}'"))
3736
};
@@ -69,7 +68,6 @@ impl SqlStatementExecutorHook {
6968
impl<F, U> ExecutorHooks<F, U> for SqlStatementExecutorHook
7069
where
7170
F: RuntimeFactors,
72-
F::AppState: Sync,
7371
{
7472
async fn configure_app(
7573
&mut self,

crates/trigger/src/cli/summary.rs

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
use spin_core::async_trait;
22
use spin_factor_key_value::KeyValueFactor;
3+
use spin_factor_sqlite::SqliteFactor;
34
use spin_factors_executor::ExecutorHooks;
45

56
use crate::factors::TriggerFactors;
67

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

911
#[async_trait]
@@ -12,13 +14,43 @@ impl<U> ExecutorHooks<TriggerFactors, U> for KeyValueDefaultStoreSummaryHook {
1214
&mut self,
1315
configured_app: &spin_factors::ConfiguredApp<TriggerFactors>,
1416
) -> anyhow::Result<()> {
15-
if let Some(default_store_summary) = configured_app
16-
.app_state::<KeyValueFactor>()
17-
.ok()
18-
.and_then(|kv_state| kv_state.store_summary("default"))
19-
{
17+
let Ok(kv_app_state) = configured_app.app_state::<KeyValueFactor>() else {
18+
return Ok(());
19+
};
20+
if !kv_app_state.store_is_used("default") {
21+
// We don't talk about unused default stores
22+
return Ok(());
23+
}
24+
if let Some(default_store_summary) = kv_app_state.store_summary("default") {
2025
println!("Storing default key-value data to {default_store_summary}.");
2126
}
2227
Ok(())
2328
}
2429
}
30+
31+
/// An [`ExecutorHooks`] that prints information about the default KV store.
32+
pub struct SqliteDefaultStoreSummaryHook;
33+
34+
#[async_trait]
35+
impl<U> ExecutorHooks<TriggerFactors, U> for SqliteDefaultStoreSummaryHook {
36+
async fn configure_app(
37+
&mut self,
38+
configured_app: &spin_factors::ConfiguredApp<TriggerFactors>,
39+
) -> anyhow::Result<()> {
40+
let Ok(sqlite_app_state) = configured_app.app_state::<SqliteFactor>() else {
41+
return Ok(());
42+
};
43+
if !sqlite_app_state.database_is_used("default") {
44+
// We don't talk about unused default databases
45+
return Ok(());
46+
}
47+
if let Some(default_database_summary) = sqlite_app_state
48+
.get_connection("default")
49+
.and_then(Result::ok)
50+
.and_then(|conn| conn.summary())
51+
{
52+
println!("Storing default SQLite data to {default_database_summary}.");
53+
}
54+
Ok(())
55+
}
56+
}

0 commit comments

Comments
 (0)