Skip to content

Commit 58d42f7

Browse files
committed
Migrate spin-redis to new core
Signed-off-by: Lann Martin <lann.martin@fermyon.com>
1 parent e035f4f commit 58d42f7

File tree

9 files changed

+94
-215
lines changed

9 files changed

+94
-215
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ spin-manifest = { path = "crates/manifest" }
3939
spin-publish = { path = "crates/publish" }
4040
spin-redis-engine = { path = "crates/redis" }
4141
spin-templates = { path = "crates/templates" }
42-
spin-trigger = { path = "crates/trigger" }
4342
spin-trigger-new = { path = "crates/trigger-new" }
4443
tempfile = "3.3.0"
4544
tokio = { version = "1.11", features = [ "full" ] }

crates/redis/Cargo.toml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ doctest = false
1111
anyhow = "1.0"
1212
async-trait = "0.1"
1313
futures = "0.3"
14-
spin-engine = { path = "../engine" }
15-
spin-manifest = { path = "../manifest" }
16-
spin-trigger = { path = "../trigger" }
14+
serde = "1"
15+
spin-app = { path = "../app" }
16+
spin-core = { path = "../core" }
17+
spin-trigger-new = { path = "../trigger-new" }
1718
redis = { version = "0.21", features = [ "tokio-comp" ] }
1819
tracing = { version = "0.1", features = [ "log" ] }
19-
wasmtime = "0.39.1"
2020

2121
[dependencies.wit-bindgen-wasmtime]
2222
git = "https://github.com/bytecodealliance/wit-bindgen"
@@ -25,4 +25,3 @@ features = ["async"]
2525

2626
[dev-dependencies]
2727
spin-testing = { path = "../testing" }
28-
tokio = { version = "1", features = [ "full" ] }

crates/redis/src/lib.rs

Lines changed: 49 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -2,100 +2,81 @@
22
33
mod spin;
44

5-
use std::{collections::HashMap, sync::Arc};
5+
use std::collections::HashMap;
66

7-
use anyhow::Result;
7+
use anyhow::{Context, Result};
88
use async_trait::async_trait;
99
use futures::StreamExt;
1010
use redis::{Client, ConnectionLike};
11-
use spin_manifest::{ComponentMap, RedisConfig, RedisTriggerConfiguration, TriggerConfig};
12-
use spin_redis::SpinRedisData;
13-
use spin_trigger::{cli::NoArgs, TriggerExecutor};
11+
use serde::{de::IgnoredAny, Deserialize, Serialize};
12+
use spin_trigger_new::{cli::NoArgs, TriggerAppEngine, TriggerExecutor};
1413

1514
use crate::spin::SpinRedisExecutor;
1615

1716
wit_bindgen_wasmtime::import!({paths: ["../../wit/ephemeral/spin-redis.wit"], async: *});
1817

19-
type ExecutionContext = spin_engine::ExecutionContext<SpinRedisData>;
20-
type RuntimeContext = spin_engine::RuntimeContext<SpinRedisData>;
18+
pub(crate) type RuntimeData = spin_redis::SpinRedisData;
19+
pub(crate) type Store = spin_core::Store<RuntimeData>;
2120

2221
/// The Spin Redis trigger.
23-
#[derive(Clone)]
2422
pub struct RedisTrigger {
25-
/// Trigger configuration.
26-
trigger_config: RedisTriggerConfiguration,
27-
/// Component trigger configurations.
28-
component_triggers: ComponentMap<RedisConfig>,
29-
/// Spin execution context.
30-
engine: Arc<ExecutionContext>,
31-
/// Map from channel name to tuple of component name & index.
32-
subscriptions: HashMap<String, usize>,
23+
engine: TriggerAppEngine<Self>,
24+
// Redis address to connect to
25+
address: String,
26+
// Mapping of subscription channels to component IDs
27+
channel_components: HashMap<String, String>,
3328
}
3429

35-
pub struct RedisTriggerConfig(String, RedisConfig);
36-
37-
impl TryFrom<(String, TriggerConfig)> for RedisTriggerConfig {
38-
type Error = spin_manifest::Error;
39-
40-
fn try_from((component, config): (String, TriggerConfig)) -> Result<Self, Self::Error> {
41-
Ok(RedisTriggerConfig(component, config.try_into()?))
42-
}
30+
/// Redis trigger configuration.
31+
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
32+
#[serde(deny_unknown_fields)]
33+
pub struct RedisTriggerConfig {
34+
/// Component ID to invoke
35+
pub component: String,
36+
/// Channel to subscribe to
37+
pub channel: String,
38+
/// Trigger executor (currently unused)
39+
#[serde(default, skip_serializing)]
40+
pub executor: IgnoredAny,
4341
}
4442

4543
#[async_trait]
4644
impl TriggerExecutor for RedisTrigger {
47-
type GlobalConfig = RedisTriggerConfiguration;
45+
const TRIGGER_TYPE: &'static str = "redis";
46+
type RuntimeData = RuntimeData;
4847
type TriggerConfig = RedisTriggerConfig;
4948
type RunConfig = NoArgs;
50-
type RuntimeContext = SpinRedisData;
51-
52-
fn new(
53-
execution_context: ExecutionContext,
54-
global_config: Self::GlobalConfig,
55-
trigger_configs: impl IntoIterator<Item = Self::TriggerConfig>,
56-
) -> Result<Self> {
57-
let component_triggers: ComponentMap<RedisConfig> = trigger_configs
58-
.into_iter()
59-
.map(|config| (config.0, config.1))
60-
.collect();
61-
let subscriptions = execution_context
62-
.config
63-
.components
64-
.iter()
65-
.enumerate()
66-
.filter_map(|(idx, component)| {
67-
component_triggers
68-
.get(&component.id)
69-
.map(|redis_config| (redis_config.channel.clone(), idx))
70-
})
49+
50+
fn new(engine: TriggerAppEngine<Self>) -> Result<Self> {
51+
let address = engine
52+
.app()
53+
.require_metadata("redis_address")
54+
.context("Failed to configure Redis trigger")?;
55+
56+
let channel_components = engine
57+
.trigger_configs()
58+
.map(|(_, config)| (config.channel.clone(), config.component.clone()))
7159
.collect();
7260

7361
Ok(Self {
74-
trigger_config: global_config,
75-
component_triggers,
76-
engine: Arc::new(execution_context),
77-
subscriptions,
62+
engine,
63+
address,
64+
channel_components,
7865
})
7966
}
8067

8168
/// Run the Redis trigger indefinitely.
8269
async fn run(self, _config: Self::RunConfig) -> Result<()> {
83-
let address = self.trigger_config.address.as_str();
70+
let address = &self.address;
8471

8572
tracing::info!("Connecting to Redis server at {}", address);
8673
let mut client = Client::open(address.to_string())?;
8774
let mut pubsub = client.get_async_connection().await?.into_pubsub();
8875

8976
// Subscribe to channels
90-
for (subscription, idx) in self.subscriptions.iter() {
91-
let name = &self.engine.config.components[*idx].id;
92-
tracing::info!(
93-
"Subscribed component #{} ({}) to channel: {}",
94-
idx,
95-
name,
96-
subscription
97-
);
98-
pubsub.subscribe(subscription).await?;
77+
for (channel, component) in self.channel_components.iter() {
78+
tracing::info!("Subscribing component {component:?} to channel {channel:?}");
79+
pubsub.subscribe(channel).await?;
9980
}
10081

10182
let mut stream = pubsub.on_message();
@@ -120,35 +101,12 @@ impl RedisTrigger {
120101
let channel = msg.get_channel_name();
121102
tracing::info!("Received message on channel {:?}", channel);
122103

123-
if let Some(idx) = self.subscriptions.get(channel).copied() {
124-
let component = &self.engine.config.components[idx];
125-
let executor = self
126-
.component_triggers
127-
.get(&component.id)
128-
.and_then(|t| t.executor.clone())
129-
.unwrap_or_default();
130-
131-
let follow = self
132-
.engine
133-
.config
134-
.follow_components
135-
.should_follow(&component.id);
136-
137-
match executor {
138-
spin_manifest::RedisExecutor::Spin => {
139-
tracing::trace!("Executing Spin Redis component {}", component.id);
140-
let executor = SpinRedisExecutor;
141-
executor
142-
.execute(
143-
&self.engine,
144-
&component.id,
145-
channel,
146-
msg.get_payload_bytes(),
147-
follow,
148-
)
149-
.await?
150-
}
151-
};
104+
if let Some(component_id) = self.channel_components.get(channel) {
105+
tracing::trace!("Executing Redis component {component_id:?}");
106+
let executor = SpinRedisExecutor;
107+
executor
108+
.execute(&self.engine, component_id, channel, msg.get_payload_bytes())
109+
.await?
152110
} else {
153111
tracing::debug!("No subscription found for {:?}", channel);
154112
}
@@ -163,11 +121,10 @@ impl RedisTrigger {
163121
pub(crate) trait RedisExecutor: Clone + Send + Sync + 'static {
164122
async fn execute(
165123
&self,
166-
engine: &ExecutionContext,
167-
component: &str,
124+
engine: &TriggerAppEngine<RedisTrigger>,
125+
component_id: &str,
168126
channel: &str,
169127
payload: &[u8],
170-
follow: bool,
171128
) -> Result<()>;
172129
}
173130

crates/redis/src/spin.rs

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
use crate::{spin_redis::SpinRedis, ExecutionContext, RedisExecutor, RuntimeContext};
2-
use anyhow::Result;
1+
use anyhow::{anyhow, Result};
32
use async_trait::async_trait;
4-
use spin_engine::io::ModuleIoRedirects;
5-
use wasmtime::{Instance, Store};
3+
use spin_core::Instance;
4+
use spin_trigger_new::TriggerAppEngine;
5+
6+
use crate::{spin_redis::SpinRedis, RedisExecutor, RedisTrigger, Store};
67

78
#[derive(Clone)]
89
pub struct SpinRedisExecutor;
@@ -11,52 +12,40 @@ pub struct SpinRedisExecutor;
1112
impl RedisExecutor for SpinRedisExecutor {
1213
async fn execute(
1314
&self,
14-
engine: &ExecutionContext,
15-
component: &str,
15+
engine: &TriggerAppEngine<RedisTrigger>,
16+
component_id: &str,
1617
channel: &str,
1718
payload: &[u8],
18-
follow: bool,
1919
) -> Result<()> {
20-
tracing::trace!(
21-
"Executing request using the Spin executor for component {}",
22-
component
23-
);
24-
25-
let mior = ModuleIoRedirects::new(follow);
20+
tracing::trace!("Executing request using the Spin executor for component {component_id}");
2621

27-
let (store, instance) = engine
28-
.prepare_component(component, None, Some(mior.pipes), None, None)
29-
.await?;
22+
let (instance, store) = engine.prepare_instance(component_id).await?;
3023

31-
let result = match Self::execute_impl(store, instance, channel, payload.to_vec()).await {
24+
match Self::execute_impl(store, instance, channel, payload.to_vec()).await {
3225
Ok(()) => {
3326
tracing::trace!("Request finished OK");
3427
Ok(())
3528
}
3629
Err(e) => {
37-
tracing::trace!("Request finished with error {}", e);
30+
tracing::trace!("Request finished with error {e}");
3831
Err(e)
3932
}
40-
};
41-
42-
let log_result =
43-
engine.save_output_to_logs(mior.read_handles.read(), component, true, true);
44-
45-
result.and(log_result)
33+
}
4634
}
4735
}
4836

4937
impl SpinRedisExecutor {
5038
pub async fn execute_impl(
51-
mut store: Store<RuntimeContext>,
39+
mut store: Store,
5240
instance: Instance,
5341
_channel: &str,
5442
payload: Vec<u8>,
5543
) -> Result<()> {
56-
let engine = SpinRedis::new(&mut store, &instance, |host| host.data.as_mut().unwrap())?;
57-
58-
let _res = engine.handle_redis_message(&mut store, &payload).await;
44+
let engine = SpinRedis::new(&mut store, &instance, |data| data.as_mut())?;
5945

60-
Ok(())
46+
engine
47+
.handle_redis_message(&mut store, &payload)
48+
.await?
49+
.map_err(|err| anyhow!("{err:?}"))
6150
}
6251
}

crates/redis/src/tests.rs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
use super::*;
22
use anyhow::Result;
33
use redis::{Msg, Value};
4-
use spin_manifest::{RedisConfig, RedisExecutor};
5-
use spin_testing::TestConfig;
6-
use spin_trigger::TriggerExecutorBuilder;
4+
use spin_testing::{tokio, TestConfig};
75

86
fn create_trigger_event(channel: &str, payload: &str) -> redis::Msg {
97
Msg::from_value(&redis::Value::Bulk(vec![
@@ -17,15 +15,11 @@ fn create_trigger_event(channel: &str, payload: &str) -> redis::Msg {
1715
#[ignore]
1816
#[tokio::test]
1917
async fn test_pubsub() -> Result<()> {
20-
let mut cfg = TestConfig::default();
21-
cfg.test_program("redis-rust.wasm")
22-
.redis_trigger(RedisConfig {
23-
channel: "messages".to_string(),
24-
executor: Some(RedisExecutor::Spin),
25-
});
26-
let app = cfg.build_application();
27-
28-
let trigger: RedisTrigger = TriggerExecutorBuilder::new(app).build().await?;
18+
let trigger: RedisTrigger = TestConfig::default()
19+
.test_program("redis-rust.wasm")
20+
.redis_trigger("messages")
21+
.build_trigger()
22+
.await;
2923

3024
let msg = create_trigger_event("messages", "hello");
3125
trigger.handle(msg).await?;

crates/testing/Cargo.toml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@ serde = "1"
1212
serde_json = "1"
1313
spin-app = { path = "../app" }
1414
spin-core = { path = "../core" }
15-
spin-engine = { path = "../engine" }
16-
spin-manifest = { path = "../manifest" }
1715
spin-http = { path = "../http" }
18-
spin-trigger = { path = "../trigger" }
1916
spin-trigger-new = { path = "../trigger-new" }
17+
tokio = { version = "1", features = ["macros", "rt"] }
2018
tracing-subscriber = "0.3"

0 commit comments

Comments
 (0)