forest/message_pool/msgpool/
pending_store.rs1use hashbrown::HashMap;
7use parking_lot::RwLock as SyncRwLock;
8use tokio::sync::broadcast;
9
10use crate::message::SignedMessage;
11use crate::message_pool::errors::Error;
12use crate::message_pool::msgpool::events::{
13 MPOOL_UPDATE_CHANNEL_CAPACITY, MpoolSubscriber, MpoolUpdate,
14};
15use crate::message_pool::msgpool::msg_pool::TrustPolicy;
16use crate::message_pool::msgpool::msg_set::{MsgSet, MsgSetLimits, StrictnessPolicy};
17use crate::prelude::*;
18use crate::shim::address::Address;
19use crate::utils::broadcast::has_subscribers;
20
21pub(in crate::message_pool) struct PendingStore {
24 inner: Arc<Inner>,
25}
26
27impl ShallowClone for PendingStore {
28 fn shallow_clone(&self) -> Self {
29 Self {
30 inner: self.inner.shallow_clone(),
31 }
32 }
33}
34
35struct Inner {
36 pending: SyncRwLock<HashMap<Address, MsgSet>>,
38 events: broadcast::Sender<MpoolUpdate>,
40 limits: MsgSetLimits,
42}
43
44impl PendingStore {
45 pub(in crate::message_pool) fn new(limits: MsgSetLimits) -> Self {
47 let (events, _) = broadcast::channel(MPOOL_UPDATE_CHANNEL_CAPACITY);
48 let inner = Arc::new(Inner {
49 pending: SyncRwLock::new(HashMap::new()),
50 events,
51 limits,
52 });
53 crate::metrics::register_collector(Box::new(InnerMetricsCollector(inner.shallow_clone())));
54 Self { inner }
55 }
56
57 pub(in crate::message_pool) fn insert(
62 &self,
63 resolved_from: Address,
64 msg: SignedMessage,
65 state_sequence: u64,
66 trust: TrustPolicy,
67 strictness: StrictnessPolicy,
68 ) -> Result<(), Error> {
69 let event_msg = has_subscribers(&self.inner.events).then(|| msg.clone());
70
71 {
72 let mut pending = self.inner.pending.write();
73 let mset = pending
74 .entry(resolved_from)
75 .or_insert_with(|| MsgSet::new(state_sequence));
76 mset.add(msg, strictness, trust, self.inner.limits)?;
77 }
78
79 if let Some(m) = event_msg {
80 let _ = self.inner.events.send(MpoolUpdate::Add(m));
82 }
83 Ok(())
84 }
85
86 pub(in crate::message_pool) fn remove(
91 &self,
92 from: &Address,
93 sequence: u64,
94 applied: bool,
95 ) -> Option<SignedMessage> {
96 let removed = {
97 let mut pending = self.inner.pending.write();
98 let mset = pending.get_mut(from)?;
99 let removed = mset.rm(sequence, applied);
100 if mset.msgs.is_empty() {
101 pending.remove(from);
102 }
103 removed
104 };
105
106 if let Some(msg) = &removed
107 && has_subscribers(&self.inner.events)
108 {
109 let _ = self.inner.events.send(MpoolUpdate::Remove(msg.clone()));
110 }
111 removed
112 }
113
114 pub(in crate::message_pool) fn shrink_to_fit(&self) {
116 self.inner.pending.write().shrink_to_fit();
117 }
118
119 pub(in crate::message_pool) fn snapshot(&self) -> HashMap<Address, MsgSet> {
121 self.inner.pending.read().clone()
122 }
123
124 pub(in crate::message_pool) fn snapshot_for(&self, addr: &Address) -> Option<MsgSet> {
126 self.inner.pending.read().get(addr).cloned()
127 }
128
129 pub(in crate::message_pool) fn subscriber(&self) -> MpoolSubscriber {
132 MpoolSubscriber::new(self.inner.events.clone())
133 }
134}
135
136#[derive(derive_more::Debug, derive_more::Deref)]
137struct InnerMetricsCollector(#[debug(skip)] Arc<Inner>);
138
139mod metrics_collection {
140 use super::*;
141 use prometheus_client::{
142 collector::Collector,
143 encoding::{DescriptorEncoder, EncodeMetric},
144 metrics::gauge::Gauge,
145 registry::Unit,
146 };
147
148 impl Collector for InnerMetricsCollector {
149 fn encode(&self, mut encoder: DescriptorEncoder) -> Result<(), std::fmt::Error> {
150 {
151 let size_in_bytes = {
152 let g: Gauge = Default::default();
153 g.set(self.pending.read().allocation_size() as i64);
154 g
155 };
156 let size_metric_encoder = encoder.encode_descriptor(
157 "mpool_pending_size",
158 "Allocation size of message pool pending messages in bytes",
159 Some(&Unit::Bytes),
160 size_in_bytes.metric_type(),
161 )?;
162 size_in_bytes.encode(size_metric_encoder)?;
163 }
164 {
165 let len = {
166 let g: Gauge = Default::default();
167 g.set(self.pending.read().len() as i64);
168 g
169 };
170 let size_metric_encoder = encoder.encode_descriptor(
171 "mpool_pending_len",
172 "Length of the message pool pending messages",
173 None,
174 len.metric_type(),
175 )?;
176 len.encode(size_metric_encoder)?;
177 }
178 {
179 let cap = {
180 let g: Gauge = Default::default();
181 g.set(self.pending.read().capacity() as i64);
182 g
183 };
184 let size_metric_encoder = encoder.encode_descriptor(
185 "mpool_pending_cap",
186 "Capacity of the message pool pending messages",
187 None,
188 cap.metric_type(),
189 )?;
190 cap.encode(size_metric_encoder)?;
191 }
192
193 Ok(())
194 }
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201 use crate::message::MessageRead as _;
202 use crate::shim::econ::TokenAmount;
203 use crate::shim::message::Message as ShimMessage;
204 use tokio::sync::broadcast::error::TryRecvError;
205
206 const TEST_LIMITS: MsgSetLimits = MsgSetLimits {
209 trusted: 1000,
210 untrusted: 1000,
211 };
212
213 fn make_smsg(from: Address, seq: u64, premium: u64) -> SignedMessage {
214 SignedMessage::mock_bls_signed_message(ShimMessage {
215 from,
216 sequence: seq,
217 gas_premium: TokenAmount::from_atto(premium),
218 gas_limit: 1_000_000,
219 ..ShimMessage::default()
220 })
221 }
222
223 fn assert_add(update: MpoolUpdate, expected_seq: u64) {
224 match update {
225 MpoolUpdate::Add(m) => assert_eq!(m.sequence(), expected_seq),
226 other => panic!("expected Add, got {other:?}"),
227 }
228 }
229
230 fn assert_remove(update: MpoolUpdate, expected_seq: u64) {
231 match update {
232 MpoolUpdate::Remove(m) => assert_eq!(m.sequence(), expected_seq),
233 other => panic!("expected Remove, got {other:?}"),
234 }
235 }
236
237 #[test]
238 fn insert_emits_add_and_stores_message() {
239 let store = PendingStore::new(TEST_LIMITS);
240 let mut rx = store.subscriber().subscribe();
241 let addr = Address::new_id(1);
242
243 store
244 .insert(
245 addr,
246 make_smsg(addr, 0, 100),
247 0,
248 TrustPolicy::Trusted,
249 StrictnessPolicy::Relaxed,
250 )
251 .unwrap();
252
253 assert_add(rx.try_recv().unwrap(), 0);
254 assert!(
255 matches!(rx.try_recv(), Err(TryRecvError::Empty)),
256 "expected empty channel"
257 );
258 assert_eq!(store.snapshot_for(&addr).unwrap().next_sequence, 1);
259 }
260
261 #[test]
262 fn rbf_replacement_emits_add_for_the_new_message() {
263 let store = PendingStore::new(TEST_LIMITS);
264 let mut rx = store.subscriber().subscribe();
265 let addr = Address::new_id(1);
266
267 store
268 .insert(
269 addr,
270 make_smsg(addr, 0, 100),
271 0,
272 TrustPolicy::Trusted,
273 StrictnessPolicy::Relaxed,
274 )
275 .unwrap();
276 store
277 .insert(
278 addr,
279 make_smsg(addr, 0, 200), 0,
281 TrustPolicy::Trusted,
282 StrictnessPolicy::Relaxed,
283 )
284 .unwrap();
285
286 assert_add(rx.try_recv().unwrap(), 0);
287 assert_add(rx.try_recv().unwrap(), 0);
288 assert!(
289 matches!(rx.try_recv(), Err(TryRecvError::Empty)),
290 "expected empty channel"
291 );
292 }
293
294 #[test]
295 fn remove_emits_remove_once_then_is_idempotent() {
296 let store = PendingStore::new(TEST_LIMITS);
297 let mut rx = store.subscriber().subscribe();
298 let addr = Address::new_id(1);
299
300 store
301 .insert(
302 addr,
303 make_smsg(addr, 0, 100),
304 0,
305 TrustPolicy::Trusted,
306 StrictnessPolicy::Relaxed,
307 )
308 .unwrap();
309 let _add = rx.try_recv().unwrap();
310
311 assert!(store.remove(&addr, 0, true).is_some());
312 assert_remove(rx.try_recv().unwrap(), 0);
313
314 assert!(store.remove(&addr, 0, true).is_none());
316 assert!(
317 matches!(rx.try_recv(), Err(TryRecvError::Empty)),
318 "expected empty channel"
319 );
320 }
321
322 #[test]
323 fn remove_of_unknown_sender_is_silent() {
324 let store = PendingStore::new(TEST_LIMITS);
325 let mut rx = store.subscriber().subscribe();
326 let addr = Address::new_id(42);
327
328 assert!(store.remove(&addr, 0, true).is_none());
329 assert!(
330 matches!(rx.try_recv(), Err(TryRecvError::Empty)),
331 "expected empty channel"
332 );
333 }
334
335 #[test]
336 fn insert_without_subscribers_skips_message_clone() {
337 let store = PendingStore::new(TEST_LIMITS);
341 let addr = Address::new_id(1);
342
343 assert!(!has_subscribers(&store.inner.events));
344 store
345 .insert(
346 addr,
347 make_smsg(addr, 0, 100),
348 0,
349 TrustPolicy::Trusted,
350 StrictnessPolicy::Relaxed,
351 )
352 .unwrap();
353 assert_eq!(store.snapshot_for(&addr).unwrap().next_sequence, 1);
354 }
355
356 #[test]
357 fn snapshot_is_a_deep_copy() {
358 let store = PendingStore::new(TEST_LIMITS);
359 let addr = Address::new_id(1);
360 store
361 .insert(
362 addr,
363 make_smsg(addr, 0, 100),
364 0,
365 TrustPolicy::Trusted,
366 StrictnessPolicy::Relaxed,
367 )
368 .unwrap();
369
370 let mut snap = store.snapshot();
371 snap.clear();
372 assert!(
373 !store.snapshot().is_empty(),
374 "mutating the snapshot must not affect the store"
375 );
376 }
377
378 #[test]
379 fn clone_is_cheap_and_shares_state() {
380 let store = PendingStore::new(TEST_LIMITS);
383 let handle = store.shallow_clone();
384 let mut rx = handle.subscriber().subscribe();
385 let addr = Address::new_id(7);
386
387 store
388 .insert(
389 addr,
390 make_smsg(addr, 0, 100),
391 0,
392 TrustPolicy::Trusted,
393 StrictnessPolicy::Relaxed,
394 )
395 .unwrap();
396
397 assert_add(rx.try_recv().unwrap(), 0);
398 assert_eq!(handle.snapshot_for(&addr).unwrap().next_sequence, 1);
399 }
400
401 #[test]
402 fn remove_clears_empty_sender_bucket() {
403 let store = PendingStore::new(TEST_LIMITS);
404 let addr = Address::new_id(1);
405 store
406 .insert(
407 addr,
408 make_smsg(addr, 0, 100),
409 0,
410 TrustPolicy::Trusted,
411 StrictnessPolicy::Relaxed,
412 )
413 .unwrap();
414 store.remove(&addr, 0, true);
415 assert!(
416 store.snapshot().is_empty(),
417 "removing the last message for an actor should drop the bucket"
418 );
419 }
420}