1
1
use outbound_redis:: * ;
2
+ use owning_ref:: RwLockReadGuardRef ;
2
3
use redis:: Commands ;
4
+ use std:: {
5
+ collections:: HashMap ,
6
+ sync:: { Arc , Mutex , RwLock } ,
7
+ } ;
3
8
4
9
pub use outbound_redis:: add_to_linker;
5
10
use spin_engine:: {
@@ -11,8 +16,9 @@ use wit_bindgen_wasmtime::wasmtime::Linker;
11
16
wit_bindgen_wasmtime:: export!( "../../wit/ephemeral/outbound-redis.wit" ) ;
12
17
13
18
/// A simple implementation to support outbound Redis commands.
14
- #[ derive( Default , Clone ) ]
15
- pub struct OutboundRedis ;
19
+ pub struct OutboundRedis {
20
+ pub connections : Arc < RwLock < HashMap < String , Mutex < redis:: Connection > > > > ,
21
+ }
16
22
17
23
impl HostComponent for OutboundRedis {
18
24
type State = Self ;
@@ -24,42 +30,87 @@ impl HostComponent for OutboundRedis {
24
30
add_to_linker ( linker, move |ctx| state_handle. get_mut ( ctx) )
25
31
}
26
32
27
- fn build_state (
28
- & self ,
29
- _component : & spin_manifest:: CoreComponent ,
30
- ) -> anyhow:: Result < Self :: State > {
31
- Ok ( Self )
33
+ fn build_state ( & self , component : & spin_manifest:: CoreComponent ) -> anyhow:: Result < Self :: State > {
34
+ let mut conn_map = HashMap :: new ( ) ;
35
+ if let Some ( address) = component. wasm . environment . get ( "REDIS_ADDRESS" ) {
36
+ let client = redis:: Client :: open ( address. to_string ( ) ) ?;
37
+ let conn = client. get_connection ( ) ?;
38
+ conn_map. insert ( address. to_owned ( ) , Mutex :: new ( conn) ) ;
39
+ }
40
+ Ok ( Self {
41
+ connections : Arc :: new ( RwLock :: new ( conn_map) ) ,
42
+ } )
32
43
}
33
44
}
34
45
35
46
impl outbound_redis:: OutboundRedis for OutboundRedis {
36
47
fn publish ( & mut self , address : & str , channel : & str , payload : & [ u8 ] ) -> Result < ( ) , Error > {
37
- let client = redis:: Client :: open ( address) . map_err ( |_| Error :: Error ) ?;
38
- let mut pubsub_conn = client. get_connection ( ) . map_err ( |_| Error :: Error ) ?;
39
- pubsub_conn
40
- . publish ( channel, payload)
48
+ let conn_map = self . get_reused_conn_map ( address) ?;
49
+ let mut conn = conn_map
50
+ . get ( address)
51
+ . unwrap ( )
52
+ . lock ( )
41
53
. map_err ( |_| Error :: Error ) ?;
54
+ conn. publish ( channel, payload) . map_err ( |_| Error :: Error ) ?;
42
55
Ok ( ( ) )
43
56
}
44
57
45
58
fn get ( & mut self , address : & str , key : & str ) -> Result < Vec < u8 > , Error > {
46
- let client = redis:: Client :: open ( address) . map_err ( |_| Error :: Error ) ?;
47
- let mut conn = client. get_connection ( ) . map_err ( |_| Error :: Error ) ?;
59
+ let conn_map = self . get_reused_conn_map ( address) ?;
60
+ let mut conn = conn_map
61
+ . get ( address)
62
+ . unwrap ( )
63
+ . lock ( )
64
+ . map_err ( |_| Error :: Error ) ?;
48
65
let value = conn. get ( key) . map_err ( |_| Error :: Error ) ?;
49
66
Ok ( value)
50
67
}
51
68
52
69
fn set ( & mut self , address : & str , key : & str , value : & [ u8 ] ) -> Result < ( ) , Error > {
53
- let client = redis:: Client :: open ( address) . map_err ( |_| Error :: Error ) ?;
54
- let mut conn = client. get_connection ( ) . map_err ( |_| Error :: Error ) ?;
70
+ let conn_map = self . get_reused_conn_map ( address) ?;
71
+ let mut conn = conn_map
72
+ . get ( address)
73
+ . unwrap ( )
74
+ . lock ( )
75
+ . map_err ( |_| Error :: Error ) ?;
55
76
conn. set ( key, value) . map_err ( |_| Error :: Error ) ?;
56
77
Ok ( ( ) )
57
78
}
58
79
59
80
fn incr ( & mut self , address : & str , key : & str ) -> Result < i64 , Error > {
60
- let client = redis:: Client :: open ( address) . map_err ( |_| Error :: Error ) ?;
61
- let mut conn = client. get_connection ( ) . map_err ( |_| Error :: Error ) ?;
81
+ let conn_map = self . get_reused_conn_map ( address) ?;
82
+ let mut conn = conn_map
83
+ . get ( address)
84
+ . unwrap ( )
85
+ . lock ( )
86
+ . map_err ( |_| Error :: Error ) ?;
62
87
let value = conn. incr ( key, 1 ) . map_err ( |_| Error :: Error ) ?;
63
88
Ok ( value)
64
89
}
65
90
}
91
+
92
+ impl OutboundRedis {
93
+ fn get_reused_conn_map < ' ret , ' me : ' ret , ' c > (
94
+ & ' me mut self ,
95
+ address : & ' c str ,
96
+ ) -> Result < RwLockReadGuardRef < ' ret , HashMap < String , Mutex < redis:: Connection > > > , Error > {
97
+ let conn_map = self . connections . read ( ) . map_err ( |_| Error :: Error ) ?;
98
+ if conn_map. get ( address) . is_some ( ) {
99
+ tracing:: debug!( "Reuse connection: {:?}" , address) ;
100
+ return Ok ( RwLockReadGuardRef :: new ( conn_map) ) ;
101
+ }
102
+ // Get rid of our read lock
103
+ drop ( conn_map) ;
104
+
105
+ let mut conn_map = self . connections . write ( ) . map_err ( |_| Error :: Error ) ?;
106
+ let client = redis:: Client :: open ( address) . map_err ( |_| Error :: Error ) ?;
107
+ let conn = client. get_connection ( ) . map_err ( |_| Error :: Error ) ?;
108
+ tracing:: debug!( "Build new connection: {:?}" , address) ;
109
+ conn_map. insert ( address. to_string ( ) , Mutex :: new ( conn) ) ;
110
+ // Get rid of our write lock
111
+ drop ( conn_map) ;
112
+
113
+ let conn_map = self . connections . read ( ) . map_err ( |_| Error :: Error ) ?;
114
+ Ok ( RwLockReadGuardRef :: new ( conn_map) )
115
+ }
116
+ }
0 commit comments