1 //! Common use patterns
2 //!
3 //! Here are some common patterns one can use for inspiration. These are mostly covered by examples
4 //! at the right type in the crate, but this lists them at a single place.
5 //!
6 //! # Sharing of configuration data
7 //!
8 //! We want to share configuration from some source with rare updates to some high performance
9 //! worker threads. It can be configuration in its true sense, or a routing table.
10 //!
11 //! The idea here is, each new version is a newly allocated in its own [`Arc`]. It is then stored
12 //! into a *shared* `ArcSwap` instance.
13 //!
14 //! Each worker then loads the current version before each work chunk. In case a new version is
15 //! stored, the worker keeps using the loaded one until it ends the work chunk and, if it's the
16 //! last one to have the version, deallocates it automatically by dropping the [`Guard`]
17 //!
18 //! Note that the configuration needs to be passed through a *single shared* [`ArcSwap`]. That
19 //! means we need to share that instance and we do so through an [`Arc`] (one could use a global
20 //! variable instead).
21 //!
22 //! Therefore, what we have is `Arc<ArcSwap<Config>>`.
23 //!
24 //! ```rust
25 //! # use std::sync::Arc;
26 //! # use std::sync::atomic::{AtomicBool, Ordering};
27 //! # use std::thread;
28 //! # use std::time::Duration;
29 //! #
30 //! # use arc_swap::ArcSwap;
31 //! # struct Work;
32 //! # impl Work { fn fetch() -> Self { Work } fn perform(&self, _: &Config) {} }
33 //! #
34 //! #[derive(Debug, Default)]
35 //! struct Config {
36 //!     // ... Stuff in here ...
37 //! }
38 //!
39 //! // We wrap the ArcSwap into an Arc, so we can share it between threads.
40 //! let config = Arc::new(ArcSwap::from_pointee(Config::default()));
41 //!
42 //! let terminate = Arc::new(AtomicBool::new(false));
43 //! let mut threads = Vec::new();
44 //!
45 //! // The configuration thread
46 //! threads.push(thread::spawn({
47 //!     let config = Arc::clone(&config);
48 //!     let terminate = Arc::clone(&terminate);
49 //!     move || {
50 //!         while !terminate.load(Ordering::Relaxed) {
51 //!             thread::sleep(Duration::from_secs(6));
52 //!             // Actually, load it from somewhere
53 //!             let new_config = Arc::new(Config::default());
54 //!             config.store(new_config);
55 //!         }
56 //!     }
57 //! }));
58 //!
59 //! // The worker thread
60 //! for _ in 0..10 {
61 //!     threads.push(thread::spawn({
62 //!         let config = Arc::clone(&config);
63 //!         let terminate = Arc::clone(&terminate);
64 //!         move || {
65 //!             while !terminate.load(Ordering::Relaxed) {
66 //!                 let work = Work::fetch();
67 //!                 let config = config.load();
68 //!                 work.perform(&config);
69 //!             }
70 //!         }
71 //!     }));
72 //! }
73 //!
74 //! // Terminate gracefully
75 //! terminate.store(true, Ordering::Relaxed);
76 //! for thread in threads {
77 //!     thread.join().unwrap();
78 //! }
79 //! ```
80 //!
81 //! # Consistent snapshots
82 //!
83 //! While one probably wants to get a fresh instance every time a work chunk is available,
84 //! therefore there would be one [`load`] for each work chunk, it is often also important that the
85 //! configuration doesn't change in the *middle* of processing of one chunk. Therefore, one
86 //! commonly wants *exactly* one [`load`] for the work chunk, not *at least* one. If the processing
87 //! had multiple phases, one would use something like this:
88 //!
89 //! ```rust
90 //! # use std::sync::Arc;
91 //! #
92 //! # use arc_swap::ArcSwap;
93 //! # struct Config;
94 //! # struct Work;
95 //! # impl Work {
96 //! #     fn fetch() -> Self { Work }
97 //! #     fn phase_1(&self, _: &Config) {}
98 //! #     fn phase_2(&self, _: &Config) {}
99 //! # }
100 //! # let config = Arc::new(ArcSwap::from_pointee(Config));
101 //! let work = Work::fetch();
102 //! let config = config.load();
103 //! work.phase_1(&config);
104 //! // We keep the same config value here
105 //! work.phase_2(&config);
106 //! ```
107 //!
108 //! Over this:
109 //!
110 //! ```rust
111 //! # use std::sync::Arc;
112 //! #
113 //! # use arc_swap::ArcSwap;
114 //! # struct Config;
115 //! # struct Work;
116 //! # impl Work {
117 //! #     fn fetch() -> Self { Work }
118 //! #     fn phase_1(&self, _: &Config) {}
119 //! #     fn phase_2(&self, _: &Config) {}
120 //! # }
121 //! # let config = Arc::new(ArcSwap::from_pointee(Config));
122 //! let work = Work::fetch();
123 //! work.phase_1(&config.load());
124 //! // WARNING!! This is broken, because in between phase_1 and phase_2, the other thread could
125 //! // have replaced the config. Then each phase would be performed with a different one and that
126 //! // could lead to surprises.
127 //! work.phase_2(&config.load());
128 //! ```
129 //!
130 //! # Caching of the configuration
131 //!
132 //! Let's say that the work chunks are really small, but there's *a lot* of them to work on. Maybe
133 //! we are routing packets and the configuration is the routing table that can sometimes change,
134 //! but mostly doesn't.
135 //!
136 //! There's an overhead to [`load`]. If the work chunks are small enough, that could be measurable.
137 //! We can reach for [`Cache`]. It makes loads much faster (in the order of accessing local
138 //! variables) in case nothing has changed. It has two costs, it makes the load slightly slower in
139 //! case the thing *did* change (which is rare) and if the worker is inactive, it holds the old
140 //! cached value alive.
141 //!
142 //! This is OK for our use case, because the routing table is usually small enough so some stale
143 //! instances taking a bit of memory isn't an issue.
144 //!
145 //! The part that takes care of updates stays the same as above.
146 //!
147 //! ```rust
148 //! # use std::sync::Arc;
149 //! # use std::thread;
150 //! # use std::sync::atomic::{AtomicBool, Ordering};
151 //! # use arc_swap::{ArcSwap, Cache};
152 //! # struct Packet; impl Packet { fn receive() -> Self { Packet } }
153 //!
154 //! #[derive(Debug, Default)]
155 //! struct RoutingTable {
156 //!     // ... Stuff in here ...
157 //! }
158 //!
159 //! impl RoutingTable {
160 //!     fn route(&self, _: Packet) {
161 //!         // ... Interesting things are done here ...
162 //!     }
163 //! }
164 //!
165 //! let routing_table = Arc::new(ArcSwap::from_pointee(RoutingTable::default()));
166 //!
167 //! let terminate = Arc::new(AtomicBool::new(false));
168 //! let mut threads = Vec::new();
169 //!
170 //! for _ in 0..10 {
171 //!     let t = thread::spawn({
172 //!         let routing_table = Arc::clone(&routing_table);
173 //!         let terminate = Arc::clone(&terminate);
174 //!         move || {
175 //!             let mut routing_table = Cache::new(routing_table);
176 //!             while !terminate.load(Ordering::Relaxed) {
177 //!                 let packet = Packet::receive();
178 //!                 // This load is cheaper, because we cache in the private Cache thing.
179 //!                 // But if the above receive takes a long time, the Cache will keep the stale
180 //!                 // value  alive until this time (when it will get replaced by up to date value).
181 //!                 let current = routing_table.load();
182 //!                 current.route(packet);
183 //!             }
184 //!         }
185 //!     });
186 //!     threads.push(t);
187 //! }
188 //!
189 //! // Shut down properly
190 //! terminate.store(true, Ordering::Relaxed);
191 //! for thread in threads {
192 //!     thread.join().unwrap();
193 //! }
194 //! ```
195 //!
196 //! # Projecting into configuration field
197 //!
198 //! We have a larger application, composed of multiple components. Each component has its own
199 //! `ComponentConfig` structure. Then, the whole application has a `Config` structure that contains
200 //! a component config for each component:
201 //!
202 //! ```rust
203 //! # struct ComponentConfig;
204 //!
205 //! struct Config {
206 //!     component: ComponentConfig,
207 //!     // ... Some other components and things ...
208 //! }
209 //! # let c = Config { component: ComponentConfig };
210 //! # let _ = c.component;
211 //! ```
212 //!
213 //! We would like to use [`ArcSwap`] to push updates to the components. But for various reasons,
214 //! it's not a good idea to put the whole `ArcSwap<Config>` to each component, eg:
215 //!
216 //! * That would make each component depend on the top level config, which feels reversed.
217 //! * It doesn't allow reusing the same component in multiple applications, as these would have
218 //!   different `Config` structures.
219 //! * One needs to build the whole `Config` for tests.
220 //! * There's a risk of entanglement, that the component would start looking at configuration of
221 //!   different parts of code, which would be hard to debug.
222 //!
223 //! We also could have a separate `ArcSwap<ComponentConfig>` for each component, but that also
224 //! doesn't feel right, as we would have to push updates to multiple places and they could be
225 //! inconsistent for a while and we would have to decompose the `Config` structure into the parts,
226 //! because we need our things in [`Arc`]s to be put into [`ArcSwap`].
227 //!
228 //! This is where the [`Access`] trait comes into play. The trait abstracts over things that can
229 //! give access to up to date version of specific T. That can be a [`Constant`] (which is useful
230 //! mostly for the tests, where one doesn't care about the updating), it can be an
231 //! [`ArcSwap<T>`][`ArcSwap`] itself, but it also can be an [`ArcSwap`] paired with a closure to
232 //! project into the specific field. The [`DynAccess`] is similar, but allows type erasure. That's
233 //! more convenient, but a little bit slower.
234 //!
235 //! ```rust
236 //! # use std::sync::Arc;
237 //! # use arc_swap::ArcSwap;
238 //! # use arc_swap::access::{DynAccess, Map};
239 //!
240 //! #[derive(Debug, Default)]
241 //! struct ComponentConfig;
242 //!
243 //! struct Component {
244 //!     config: Box<dyn DynAccess<ComponentConfig>>,
245 //! }
246 //!
247 //! #[derive(Debug, Default)]
248 //! struct Config {
249 //!     component: ComponentConfig,
250 //! }
251 //!
252 //! let config = Arc::new(ArcSwap::from_pointee(Config::default()));
253 //!
254 //! let component = Component {
255 //!     config: Box::new(Map::new(Arc::clone(&config), |config: &Config| &config.component)),
256 //! };
257 //! # let _ = component.config;
258 //! ```
259 //!
260 //! One would use `Box::new(Constant(ComponentConfig))` in unittests instead as the `config` field.
261 //!
262 //! The [`Cache`] has its own [`Access`][crate::cache::Access] trait for similar purposes.
263 //!
264 //! [`Arc`]: std::sync::Arc
265 //! [`Guard`]: crate::Guard
266 //! [`load`]: crate::ArcSwapAny::load
267 //! [`ArcSwap`]: crate::ArcSwap
268 //! [`Cache`]: crate::cache::Cache
269 //! [`Access`]: crate::access::Access
270 //! [`DynAccess`]: crate::access::DynAccess
271 //! [`Constant`]: crate::access::Constant
272