2
2
3
3
mod spin;
4
4
5
- use std:: { collections:: HashMap , sync :: Arc } ;
5
+ use std:: collections:: HashMap ;
6
6
7
- use anyhow:: Result ;
7
+ use anyhow:: { Context , Result } ;
8
8
use async_trait:: async_trait;
9
9
use futures:: StreamExt ;
10
10
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 } ;
14
13
15
14
use crate :: spin:: SpinRedisExecutor ;
16
15
17
16
wit_bindgen_wasmtime:: import!( { paths: [ "../../wit/ephemeral/spin-redis.wit" ] , async : * } ) ;
18
17
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 > ;
21
20
22
21
/// The Spin Redis trigger.
23
- #[ derive( Clone ) ]
24
22
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 > ,
33
28
}
34
29
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 ,
43
41
}
44
42
45
43
#[ async_trait]
46
44
impl TriggerExecutor for RedisTrigger {
47
- type GlobalConfig = RedisTriggerConfiguration ;
45
+ const TRIGGER_TYPE : & ' static str = "redis" ;
46
+ type RuntimeData = RuntimeData ;
48
47
type TriggerConfig = RedisTriggerConfig ;
49
48
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 ( ) ) )
71
59
. collect ( ) ;
72
60
73
61
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,
78
65
} )
79
66
}
80
67
81
68
/// Run the Redis trigger indefinitely.
82
69
async fn run ( self , _config : Self :: RunConfig ) -> Result < ( ) > {
83
- let address = self . trigger_config . address . as_str ( ) ;
70
+ let address = & self . address ;
84
71
85
72
tracing:: info!( "Connecting to Redis server at {}" , address) ;
86
73
let mut client = Client :: open ( address. to_string ( ) ) ?;
87
74
let mut pubsub = client. get_async_connection ( ) . await ?. into_pubsub ( ) ;
88
75
89
76
// 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 ?;
99
80
}
100
81
101
82
let mut stream = pubsub. on_message ( ) ;
@@ -120,35 +101,12 @@ impl RedisTrigger {
120
101
let channel = msg. get_channel_name ( ) ;
121
102
tracing:: info!( "Received message on channel {:?}" , channel) ;
122
103
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 ?
152
110
} else {
153
111
tracing:: debug!( "No subscription found for {:?}" , channel) ;
154
112
}
@@ -163,11 +121,10 @@ impl RedisTrigger {
163
121
pub ( crate ) trait RedisExecutor : Clone + Send + Sync + ' static {
164
122
async fn execute (
165
123
& self ,
166
- engine : & ExecutionContext ,
167
- component : & str ,
124
+ engine : & TriggerAppEngine < RedisTrigger > ,
125
+ component_id : & str ,
168
126
channel : & str ,
169
127
payload : & [ u8 ] ,
170
- follow : bool ,
171
128
) -> Result < ( ) > ;
172
129
}
173
130
0 commit comments