1
1
use futures03:: TryStreamExt ;
2
+ use graph:: parking_lot:: Mutex ;
2
3
use graph:: tokio_stream:: wrappers:: ReceiverStream ;
4
+ use std:: collections:: BTreeSet ;
3
5
use std:: sync:: { atomic:: Ordering , Arc , RwLock } ;
4
6
use std:: { collections:: HashMap , sync:: atomic:: AtomicUsize } ;
5
7
use tokio:: sync:: mpsc:: { channel, Sender } ;
@@ -84,38 +86,46 @@ impl StoreEventListener {
84
86
}
85
87
}
86
88
87
- #[ async_trait]
88
- trait EventSink : Send + Sync {
89
- async fn send ( & self , event : Arc < StoreEvent > ) -> Result < ( ) , Error > ;
90
- fn is_closed ( & self ) -> bool ;
89
+ struct Watcher < T > {
90
+ sender : Arc < watch:: Sender < T > > ,
91
+ receiver : watch:: Receiver < T > ,
91
92
}
92
93
93
- #[ async_trait]
94
- impl EventSink for Sender < Arc < StoreEvent > > {
95
- async fn send ( & self , event : Arc < StoreEvent > ) -> Result < ( ) , Error > {
96
- Ok ( self . send ( event) . await ?)
94
+ impl < T : Clone + Debug + Send + Sync + ' static > Watcher < T > {
95
+ fn new ( init : T ) -> Self {
96
+ let ( sender, receiver) = watch:: channel ( init) ;
97
+ Watcher {
98
+ sender : Arc :: new ( sender) ,
99
+ receiver,
100
+ }
97
101
}
98
102
99
- fn is_closed ( & self ) -> bool {
100
- self . is_closed ( )
103
+ fn send ( & self , v : T ) {
104
+ // Unwrap: `self` holds a receiver.
105
+ self . sender . send ( v) . unwrap ( )
101
106
}
102
- }
103
107
104
- # [ async_trait ]
105
- impl EventSink for watch :: Sender < ( ) > {
106
- async fn send ( & self , _event : Arc < StoreEvent > ) -> Result < ( ) , Error > {
107
- Ok ( self . send ( ( ) ) ? )
108
+ fn stream ( & self ) -> Box < dyn futures03 :: Stream < Item = T > + Unpin + Send + Sync > {
109
+ Box :: new ( tokio_stream :: wrappers :: WatchStream :: new (
110
+ self . receiver . clone ( ) ,
111
+ ) )
108
112
}
109
113
110
- fn is_closed ( & self ) -> bool {
111
- self . is_closed ( )
114
+ /// Outstanding receivers returned from `Self::stream`.
115
+ fn receiver_count ( & self ) -> usize {
116
+ // Do not count the internal receiver.
117
+ self . sender . receiver_count ( ) - 1
112
118
}
113
119
}
114
120
115
121
/// Manage subscriptions to the `StoreEvent` stream. Keep a list of
116
122
/// currently active subscribers and forward new events to each of them
117
123
pub struct SubscriptionManager {
118
- subscriptions : Arc < RwLock < HashMap < String , ( Arc < Vec < SubscriptionFilter > > , Arc < dyn EventSink > ) > > > ,
124
+ // These are more efficient since only one entry is stored per filter.
125
+ subscriptions_no_payload : Arc < Mutex < HashMap < BTreeSet < SubscriptionFilter > , Watcher < ( ) > > > > ,
126
+
127
+ subscriptions :
128
+ Arc < RwLock < HashMap < String , ( Arc < BTreeSet < SubscriptionFilter > > , Sender < Arc < StoreEvent > > ) > > > ,
119
129
120
130
/// Keep the notification listener alive
121
131
listener : StoreEventListener ,
@@ -126,6 +136,7 @@ impl SubscriptionManager {
126
136
let ( listener, store_events) = StoreEventListener :: new ( logger, postgres_url, registry) ;
127
137
128
138
let mut manager = SubscriptionManager {
139
+ subscriptions_no_payload : Arc :: new ( Mutex :: new ( HashMap :: new ( ) ) ) ,
129
140
subscriptions : Arc :: new ( RwLock :: new ( HashMap :: new ( ) ) ) ,
130
141
listener,
131
142
} ;
@@ -146,61 +157,101 @@ impl SubscriptionManager {
146
157
& self ,
147
158
store_events : Box < dyn Stream < Item = StoreEvent , Error = ( ) > + Send > ,
148
159
) {
149
- let subscriptions = self . subscriptions . clone ( ) ;
160
+ let subscriptions = self . subscriptions . cheap_clone ( ) ;
161
+ let subscriptions_no_payload = self . subscriptions_no_payload . cheap_clone ( ) ;
150
162
let mut store_events = store_events. compat ( ) ;
151
163
152
164
// This channel is constantly receiving things and there are locks involved,
153
165
// so it's best to use a blocking task.
154
166
graph:: spawn_blocking ( async move {
155
167
while let Some ( Ok ( event) ) = store_events. next ( ) . await {
156
- let senders = subscriptions. read ( ) . unwrap ( ) . clone ( ) ;
157
168
let event = Arc :: new ( event) ;
158
169
159
- // Write change to all matching subscription streams; remove subscriptions
160
- // whose receiving end has been dropped
161
- for ( id, ( _, sender) ) in senders
162
- . iter ( )
163
- . filter ( |( _, ( filter, _) ) | event. matches ( filter) )
170
+ // Send to `subscriptions`.
164
171
{
165
- if sender. send ( event. cheap_clone ( ) ) . await . is_err ( ) {
166
- // Receiver was dropped
167
- subscriptions. write ( ) . unwrap ( ) . remove ( id) ;
172
+ let senders = subscriptions. read ( ) . unwrap ( ) . clone ( ) ;
173
+
174
+ // Write change to all matching subscription streams; remove subscriptions
175
+ // whose receiving end has been dropped
176
+ for ( id, ( _, sender) ) in senders
177
+ . iter ( )
178
+ . filter ( |( _, ( filter, _) ) | event. matches ( filter) )
179
+ {
180
+ if sender. send ( event. cheap_clone ( ) ) . await . is_err ( ) {
181
+ // Receiver was dropped
182
+ subscriptions. write ( ) . unwrap ( ) . remove ( id) ;
183
+ }
184
+ }
185
+ }
186
+
187
+ // Send to `subscriptions_no_payload`.
188
+ {
189
+ let watchers = subscriptions_no_payload. lock ( ) ;
190
+
191
+ // Write change to all matching subscription streams
192
+ for ( _, watcher) in watchers. iter ( ) . filter ( |( filter, _) | event. matches ( filter) )
193
+ {
194
+ watcher. send ( ( ) ) ;
168
195
}
169
196
}
170
197
}
171
198
} ) ;
172
199
}
173
200
174
201
fn periodically_clean_up_stale_subscriptions ( & self ) {
175
- let subscriptions = self . subscriptions . clone ( ) ;
202
+ let subscriptions = self . subscriptions . cheap_clone ( ) ;
203
+ let subscriptions_no_payload = self . subscriptions_no_payload . cheap_clone ( ) ;
176
204
177
205
// Clean up stale subscriptions every 5s
178
206
graph:: spawn ( async move {
179
207
let mut interval = tokio:: time:: interval ( Duration :: from_secs ( 5 ) ) ;
180
208
loop {
181
209
interval. tick ( ) . await ;
182
- let mut subscriptions = subscriptions. write ( ) . unwrap ( ) ;
183
-
184
- // Obtain IDs of subscriptions whose receiving end has gone
185
- let stale_ids = subscriptions
186
- . iter_mut ( )
187
- . filter_map ( |( id, ( _, sender) ) | match sender. is_closed ( ) {
188
- true => Some ( id. clone ( ) ) ,
189
- false => None ,
190
- } )
191
- . collect :: < Vec < _ > > ( ) ;
192
-
193
- // Remove all stale subscriptions
194
- for id in stale_ids {
195
- subscriptions. remove ( & id) ;
210
+
211
+ // Cleanup `subscriptions`.
212
+ {
213
+ let mut subscriptions = subscriptions. write ( ) . unwrap ( ) ;
214
+
215
+ // Obtain IDs of subscriptions whose receiving end has gone
216
+ let stale_ids = subscriptions
217
+ . iter_mut ( )
218
+ . filter_map ( |( id, ( _, sender) ) | match sender. is_closed ( ) {
219
+ true => Some ( id. clone ( ) ) ,
220
+ false => None ,
221
+ } )
222
+ . collect :: < Vec < _ > > ( ) ;
223
+
224
+ // Remove all stale subscriptions
225
+ for id in stale_ids {
226
+ subscriptions. remove ( & id) ;
227
+ }
228
+ }
229
+
230
+ // Cleanup `subscriptions_no_payload`.
231
+ {
232
+ let mut subscriptions = subscriptions_no_payload. lock ( ) ;
233
+
234
+ // Obtain IDs of subscriptions whose receiving end has gone
235
+ let stale_ids = subscriptions
236
+ . iter_mut ( )
237
+ . filter_map ( |( id, watcher) | match watcher. receiver_count ( ) == 0 {
238
+ true => Some ( id. clone ( ) ) ,
239
+ false => None ,
240
+ } )
241
+ . collect :: < Vec < _ > > ( ) ;
242
+
243
+ // Remove all stale subscriptions
244
+ for id in stale_ids {
245
+ subscriptions. remove ( & id) ;
246
+ }
196
247
}
197
248
}
198
249
} ) ;
199
250
}
200
251
}
201
252
202
253
impl SubscriptionManagerTrait for SubscriptionManager {
203
- fn subscribe ( & self , entities : Vec < SubscriptionFilter > ) -> StoreEventStreamBox {
254
+ fn subscribe ( & self , entities : BTreeSet < SubscriptionFilter > ) -> StoreEventStreamBox {
204
255
let id = Uuid :: new_v4 ( ) . to_string ( ) ;
205
256
206
257
// Prepare the new subscription by creating a channel and a subscription object
@@ -210,23 +261,18 @@ impl SubscriptionManagerTrait for SubscriptionManager {
210
261
self . subscriptions
211
262
. write ( )
212
263
. unwrap ( )
213
- . insert ( id, ( Arc :: new ( entities. clone ( ) ) , Arc :: new ( sender) ) ) ;
264
+ . insert ( id, ( Arc :: new ( entities. clone ( ) ) , sender) ) ;
214
265
215
266
// Return the subscription ID and entity change stream
216
267
StoreEventStream :: new ( Box :: new ( ReceiverStream :: new ( receiver) . map ( Ok ) . compat ( ) ) )
217
268
. filter_by_entities ( entities)
218
269
}
219
270
220
- fn subscribe_no_payload ( & self , entities : Vec < SubscriptionFilter > ) -> UnitStream {
221
- let id = Uuid :: new_v4 ( ) . to_string ( ) ;
222
-
223
- let ( sender, receiver) = watch:: channel ( ( ) ) ;
224
-
225
- self . subscriptions
226
- . write ( )
227
- . unwrap ( )
228
- . insert ( id, ( Arc :: new ( entities. clone ( ) ) , Arc :: new ( sender) ) ) ;
229
-
230
- Box :: new ( tokio_stream:: wrappers:: WatchStream :: new ( receiver) )
271
+ fn subscribe_no_payload ( & self , entities : BTreeSet < SubscriptionFilter > ) -> UnitStream {
272
+ self . subscriptions_no_payload
273
+ . lock ( )
274
+ . entry ( entities)
275
+ . or_insert_with ( || Watcher :: new ( ( ) ) )
276
+ . stream ( )
231
277
}
232
278
}
0 commit comments