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