arc_swap/docs/
patterns.rs

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