Skip to content

Commit 31aa7ec

Browse files
authored
Merge pull request #2843 from fermyon/key-value-re-factoring
Key Value re-*factor*ing
2 parents 1e3d971 + 5db8565 commit 31aa7ec

File tree

14 files changed

+306
-369
lines changed

14 files changed

+306
-369
lines changed

Cargo.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/factor-key-value/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ tracing = { workspace = true }
1919

2020
[dev-dependencies]
2121
spin-factors-test = { path = "../factors-test" }
22-
spin-key-value-redis = { path = "../key-value-redis" }
2322
spin-key-value-spin = { path = "../key-value-spin" }
23+
spin-key-value-redis = { path = "../key-value-redis" }
2424
tempfile = "3.12.0"
2525
tokio = { version = "1", features = ["macros", "rt"] }
2626

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

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use crate::util::EmptyStoreManager;
21
use anyhow::{Context, Result};
32
use spin_core::{async_trait, wasmtime::component::Resource};
43
use spin_world::v2::key_value;
@@ -40,23 +39,22 @@ pub struct KeyValueDispatch {
4039
}
4140

4241
impl KeyValueDispatch {
43-
pub fn new() -> Self {
44-
Self::new_with_capacity(DEFAULT_STORE_TABLE_CAPACITY)
42+
pub fn new(allowed_stores: HashSet<String>, manager: Arc<dyn StoreManager>) -> Self {
43+
Self::new_with_capacity(allowed_stores, manager, DEFAULT_STORE_TABLE_CAPACITY)
4544
}
4645

47-
pub fn new_with_capacity(capacity: u32) -> Self {
46+
pub fn new_with_capacity(
47+
allowed_stores: HashSet<String>,
48+
manager: Arc<dyn StoreManager>,
49+
capacity: u32,
50+
) -> Self {
4851
Self {
49-
allowed_stores: HashSet::new(),
50-
manager: Arc::new(EmptyStoreManager),
52+
allowed_stores,
53+
manager,
5154
stores: Table::new(capacity),
5255
}
5356
}
5457

55-
pub fn init(&mut self, allowed_stores: HashSet<String>, manager: Arc<dyn StoreManager>) {
56-
self.allowed_stores = allowed_stores;
57-
self.manager = manager;
58-
}
59-
6058
pub fn get_store(&self, store: Resource<key_value::Store>) -> anyhow::Result<&Arc<dyn Store>> {
6159
self.stores.get(store.rep()).context("invalid store")
6260
}
@@ -66,12 +64,6 @@ impl KeyValueDispatch {
6664
}
6765
}
6866

69-
impl Default for KeyValueDispatch {
70-
fn default() -> Self {
71-
Self::new()
72-
}
73-
}
74-
7567
#[async_trait]
7668
impl key_value::Host for KeyValueDispatch {}
7769

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

Lines changed: 13 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use spin_factors::{
1212
ConfigureAppContext, Factor, FactorInstanceBuilder, InitContext, PrepareContext, RuntimeFactors,
1313
};
1414
use spin_locked_app::MetadataKey;
15-
use util::DefaultManagerGetter;
1615

1716
/// Metadata key for key-value stores.
1817
pub const KEY_VALUE_STORES_KEY: MetadataKey<Vec<String>> = MetadataKey::new("key_value_stores");
@@ -21,19 +20,15 @@ pub use runtime_config::RuntimeConfig;
2120
pub use util::{CachingStoreManager, DelegatingStoreManager};
2221

2322
/// A factor that provides key-value storage.
23+
#[derive(Default)]
2424
pub struct KeyValueFactor {
25-
default_label_resolver: Arc<dyn DefaultLabelResolver>,
25+
_priv: (),
2626
}
2727

2828
impl KeyValueFactor {
2929
/// Create a new KeyValueFactor.
30-
///
31-
/// The `default_label_resolver` is used to resolve store managers for labels that
32-
/// are not defined in the runtime configuration.
33-
pub fn new(default_label_resolver: impl DefaultLabelResolver + 'static) -> Self {
34-
Self {
35-
default_label_resolver: Arc::new(default_label_resolver),
36-
}
30+
pub fn new() -> Self {
31+
Self { _priv: () }
3732
}
3833
}
3934

@@ -53,13 +48,10 @@ impl Factor for KeyValueFactor {
5348
mut ctx: ConfigureAppContext<T, Self>,
5449
) -> anyhow::Result<Self::AppState> {
5550
let store_managers = ctx.take_runtime_config().unwrap_or_default();
56-
let default_label_resolver = self.default_label_resolver.clone();
57-
let default_fn: DefaultManagerGetter =
58-
Arc::new(move |label| default_label_resolver.default(label));
5951

60-
let delegating_manager = DelegatingStoreManager::new(store_managers, default_fn);
52+
let delegating_manager = DelegatingStoreManager::new(store_managers);
6153
let caching_manager = CachingStoreManager::new(delegating_manager);
62-
let store_manager_manager = Arc::new(caching_manager);
54+
let store_manager = Arc::new(caching_manager);
6355

6456
// Build component -> allowed stores map
6557
let mut component_allowed_stores = HashMap::new();
@@ -73,8 +65,7 @@ impl Factor for KeyValueFactor {
7365
for label in &key_value_stores {
7466
// TODO: port nicer errors from KeyValueComponent (via error type?)
7567
ensure!(
76-
store_manager_manager.is_defined(label)
77-
|| self.default_label_resolver.default(label).is_some(),
68+
store_manager.is_defined(label),
7869
"unknown key_value_stores label {label:?} for component {component_id:?}"
7970
);
8071
}
@@ -83,7 +74,7 @@ impl Factor for KeyValueFactor {
8374
}
8475

8576
Ok(AppState {
86-
store_manager: store_manager_manager,
77+
store_manager,
8778
component_allowed_stores,
8879
})
8980
}
@@ -159,22 +150,10 @@ impl FactorInstanceBuilder for InstanceBuilder {
159150
store_manager,
160151
allowed_stores,
161152
} = self;
162-
let mut dispatch = KeyValueDispatch::new_with_capacity(u32::MAX);
163-
dispatch.init(allowed_stores, store_manager);
164-
Ok(dispatch)
165-
}
166-
}
167-
168-
/// Resolves a label to a default [`StoreManager`].
169-
pub trait DefaultLabelResolver: Send + Sync {
170-
/// If there is no runtime configuration for a given store label, return a default store manager.
171-
///
172-
/// If `Option::None` is returned, the store is not allowed.
173-
fn default(&self, label: &str) -> Option<Arc<dyn StoreManager>>;
174-
}
175-
176-
impl<T: DefaultLabelResolver> DefaultLabelResolver for Arc<T> {
177-
fn default(&self, label: &str) -> Option<Arc<dyn StoreManager>> {
178-
self.as_ref().default(label)
153+
Ok(KeyValueDispatch::new_with_capacity(
154+
allowed_stores,
155+
store_manager,
156+
u32::MAX,
157+
))
179158
}
180159
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,16 @@ impl RuntimeConfig {
1818
pub fn add_store_manager(&mut self, label: String, store_manager: Arc<dyn StoreManager>) {
1919
self.store_managers.insert(label, store_manager);
2020
}
21+
22+
/// Returns whether a store manager exists for the store with the given label.
23+
pub fn has_store_manager(&self, label: &str) -> bool {
24+
self.store_managers.contains_key(label)
25+
}
26+
27+
/// Returns the store manager for the store with the given label.
28+
pub fn get_store_manager(&self, label: &str) -> Option<Arc<dyn StoreManager>> {
29+
self.store_managers.get(label).cloned()
30+
}
2131
}
2232

2333
impl IntoIterator for RuntimeConfig {

crates/factor-key-value/src/runtime_config/spin.rs

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
//! Runtime configuration implementation used by Spin CLI.
22
3-
use crate::StoreManager;
4-
use crate::{DefaultLabelResolver, RuntimeConfig};
3+
use crate::{RuntimeConfig, StoreManager};
54
use anyhow::Context as _;
65
use serde::de::DeserializeOwned;
76
use serde::{Deserialize, Serialize};
@@ -99,7 +98,25 @@ impl RuntimeConfigResolver {
9998
}
10099

101100
/// Resolves a toml table into a runtime config.
102-
pub fn resolve_from_toml(
101+
///
102+
/// The default stores are also added to the runtime config.
103+
pub fn resolve(&self, table: Option<&impl GetTomlValue>) -> anyhow::Result<RuntimeConfig> {
104+
let mut runtime_config = self.resolve_from_toml(table)?.unwrap_or_default();
105+
106+
for (&label, config) in &self.defaults {
107+
if !runtime_config.store_managers.contains_key(label) {
108+
let store_manager = self
109+
.store_manager_from_config(config.clone())
110+
.with_context(|| {
111+
format!("could not configure key-value store with label '{label}'")
112+
})?;
113+
runtime_config.add_store_manager(label.to_owned(), store_manager);
114+
}
115+
}
116+
Ok(runtime_config)
117+
}
118+
119+
fn resolve_from_toml(
103120
&self,
104121
table: Option<&impl GetTomlValue>,
105122
) -> anyhow::Result<Option<RuntimeConfig>> {
@@ -115,6 +132,7 @@ impl RuntimeConfigResolver {
115132
})?;
116133
runtime_config.add_store_manager(label.clone(), store_manager);
117134
}
135+
118136
Ok(Some(runtime_config))
119137
}
120138

@@ -134,15 +152,6 @@ impl RuntimeConfigResolver {
134152
}
135153
}
136154

137-
impl DefaultLabelResolver for RuntimeConfigResolver {
138-
fn default(&self, label: &str) -> Option<Arc<dyn StoreManager>> {
139-
let config = self.defaults.get(label)?;
140-
// TODO(rylev): The unwrap here is not ideal. We should return a Result instead.
141-
// Piping that through `DefaultLabelResolver` is a bit awkward, though.
142-
Some(self.store_manager_from_config(config.clone()).unwrap())
143-
}
144-
}
145-
146155
#[derive(Deserialize, Clone)]
147156
pub struct StoreConfig {
148157
#[serde(rename = "type")]

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

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13,39 +13,15 @@ use tokio::{
1313
};
1414
use tracing::Instrument;
1515

16-
const DEFAULT_CACHE_SIZE: usize = 256;
17-
18-
pub struct EmptyStoreManager;
19-
20-
#[async_trait]
21-
impl StoreManager for EmptyStoreManager {
22-
async fn get(&self, _name: &str) -> Result<Arc<dyn Store>, Error> {
23-
Err(Error::NoSuchStore)
24-
}
25-
26-
fn is_defined(&self, _store_name: &str) -> bool {
27-
false
28-
}
29-
}
30-
31-
/// A function that takes a store label and returns the default store manager, if one exists.
32-
pub type DefaultManagerGetter = Arc<dyn Fn(&str) -> Option<Arc<dyn StoreManager>> + Send + Sync>;
33-
16+
/// A [`StoreManager`] which delegates to other `StoreManager`s based on the store label.
3417
pub struct DelegatingStoreManager {
3518
delegates: HashMap<String, Arc<dyn StoreManager>>,
36-
default_manager: DefaultManagerGetter,
3719
}
3820

3921
impl DelegatingStoreManager {
40-
pub fn new(
41-
delegates: impl IntoIterator<Item = (String, Arc<dyn StoreManager>)>,
42-
default_manager: DefaultManagerGetter,
43-
) -> Self {
22+
pub fn new(delegates: impl IntoIterator<Item = (String, Arc<dyn StoreManager>)>) -> Self {
4423
let delegates = delegates.into_iter().collect();
45-
Self {
46-
delegates,
47-
default_manager,
48-
}
24+
Self { delegates }
4925
}
5026
}
5127

@@ -54,12 +30,7 @@ impl StoreManager for DelegatingStoreManager {
5430
async fn get(&self, name: &str) -> Result<Arc<dyn Store>, Error> {
5531
match self.delegates.get(name) {
5632
Some(store) => store.get(name).await,
57-
None => {
58-
(self.default_manager)(name)
59-
.ok_or(Error::NoSuchStore)?
60-
.get(name)
61-
.await
62-
}
33+
None => Err(Error::NoSuchStore),
6334
}
6435
}
6536

@@ -71,7 +42,7 @@ impl StoreManager for DelegatingStoreManager {
7142
if let Some(store) = self.delegates.get(store_name) {
7243
return store.summary(store_name);
7344
}
74-
(self.default_manager)(store_name)?.summary(store_name)
45+
None
7546
}
7647
}
7748

@@ -104,6 +75,8 @@ pub struct CachingStoreManager<T> {
10475
inner: T,
10576
}
10677

78+
const DEFAULT_CACHE_SIZE: usize = 256;
79+
10780
impl<T> CachingStoreManager<T> {
10881
pub fn new(inner: T) -> Self {
10982
Self::new_with_capacity(NonZeroUsize::new(DEFAULT_CACHE_SIZE).unwrap(), inner)

0 commit comments

Comments
 (0)