Skip to main content

Crate dynamic_config_web_core

Crate dynamic_config_web_core 

Source
Expand description

One reading of each configuration section, taken when a request begins.

Config::current() is an atomic load, and its own documentation says to call it once per request: a reload landing between two calls lets one request observe two configurations. With one section that is easy to honour. With two it is not, because “the same generation” is a property of a pair of reads that no single call site can see.

This crate is the pair. A Sections list is read once into a Snapshot, the framework adapter puts that snapshot where the request can reach it, and every handler read comes back out of it.

use std::sync::Arc;
use dynamic_config_web_core::Sections;

#[derive(Debug, PartialEq)]
struct Server { port: u16 }
#[derive(Debug, PartialEq)]
struct Features { cache: bool }

let sections = Sections::new()
    .section({ let it = Arc::clone(&server); move || Some(Arc::clone(&it)) })
    .section({ let it = Arc::clone(&features); move || Some(Arc::clone(&it)) });

let snapshot = sections.take();

assert_eq!(snapshot.get::<Server>().unwrap().port, 8080);
assert!(snapshot.get::<Features>().unwrap().cache);

In a service the closures are || ServerConfig::try_current(), which the sections! macro writes for you.

§How one reading stays one reading

Each configuration has its own atomic cell and the engine keeps no epoch across them, so reading N sections is N independent loads — and a reload landing between two of them would put two generations in one snapshot, which is the bug this crate exists to prevent.

Sections::take therefore reads the install counters, reads the sections, and reads the counters again; if anything moved it starts over. A section registered through Sections::section supplies no counter, and a list containing one reads without the check — Sections::is_consistent says which kind you have, and the sections! macro always produces the checked kind.

§Why closures rather than a trait

#[dynamic_config] writes try_current() as an inherent method, so a generic function cannot call it. A closure can, and the same shape covers a Dynamic<T> instance — move || handle.current() — which a trait implemented on the type could not reach.

Macros§

sections
The sections a request reads, by type.

Structs§

Sections
The sections a request reads, and how to read each one.
Snapshot
What one request may read: one Arc per section, taken together.

Enums§

NotInScope
Why a section is not in this request’s snapshot.