1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//! Shared scaffolding for the chain-backed DHT projections (#1255).
//!
//! Each of [`ChainStakerSet`](super::chain_staker_set::ChainStakerSet),
//! [`ChainNodeAddressDirectory`](super::node_address::ChainNodeAddressDirectory),
//! and [`ChainOriginDirectory`](super::chain_origin_directory::ChainOriginDirectory)
//! caches chain state behind an `RwLock`, reads it on the hot path with
//! poison-tolerant recovery, republishes a size gauge only on a *real* mutation,
//! and owns the background watcher task that keeps the cache fresh. This module
//! factors those three pieces out:
//!
//! - [`with_read`] / [`with_write`] — the poison-recovery lock idiom, one
//! `warn!` site, `label` naming the projection.
//! - [`with_lock`] — the same idiom for a `Mutex`: the DHT republish scheduler's
//! heap and its live-hash map, and the DHT routing table.
//! - [`mutate_gauged`] — mutate under the write lock, sample the size while
//! still holding it, and republish the gauge only when the mutation actually
//! changed the set (a re-scanned `eth_getLogs` window replays no-ops).
//! - [`ChainProjection`] — the façade-held bundle of the shared state, exposing
//! the read seam.
//!
//! The mutation helpers stay free functions over `&RwLock<T>` because the
//! *sink* — which lives inside the shared poller task, so it cannot reach back
//! into the [`ChainProjection`] — is what applies mutations, holding its own
//! `Arc<RwLock<T>>` clone made in `bootstrap`.
use ;
use warn;
/// Poison-tolerant `RwLock` read. A poisoned lock means something panicked while
/// holding the write guard; the cached state is still structurally valid (no
/// panic is possible mid-mutation here), so recover the inner value rather than
/// propagate a panic into the read hot path. Log on the recovery arm so the
/// original panic surfaces somewhere, matching the precedent in
/// `cache/src/engine.rs`.
///
/// The recovery arm clears the poison, so the warning is one line per panic
/// rather than one per acquisition. Poison is otherwise sticky, and these locks
/// sit on paths that run per inbound request and on a 1 Hz timer — an ungated
/// log there is the megabytes-per-second flood `dispatch`'s own poison gate
/// exists to prevent.
pub
/// Poison-tolerant `RwLock` write, mirroring [`with_read`].
pub
/// Poison-tolerant `Mutex` lock, mirroring [`with_write`] for state behind a
/// plain `Mutex`. Nest two calls to hold a pair of locks; the outer call is
/// taken first, so the nesting order is the lock order.
///
/// `pub(crate)` rather than `pub(super)` because the DHT routing table is
/// locked from the request handler too, and every acquisition of one `Mutex`
/// must share one recovery idiom: a site that recovers less than the others
/// turns a single panic into split-brain, where the republisher keeps working
/// on a table the query path answers empty for.
pub
/// Apply `mutate` under the write lock and republish a size gauge only on a real
/// change. `mutate` returns `Some(sample)` when it actually changed the state
/// (and the size to publish, sampled while the lock is held so the gauge never
/// observes a torn view) or `None` for an idempotent no-op — a re-insert or an
/// absent-remove, which a re-scanned `eth_getLogs` window produces routinely.
/// Returns whether it mutated, mirroring the underlying collection op's bool.
pub
/// The shared state behind a chain-backed projection. Cheap to build via
/// [`from_parts`](ChainProjection::from_parts). The background loop that keeps
/// the state fresh is the single [`multiplexed_poller`] task the runtime owns
/// and shuts down; the projection holds no per-watcher handle.
///
/// [`multiplexed_poller`]: crate::chain_events::multiplexed_poller
pub