Configuration
When the main
function runs, the first thing it does is to load the configuration.
The configuration is a YAML file with a main part and a section for each of the layers.
version: 0.1
spec:
agent:
kind: Default
port: 3097
state:
kind: Default
ttl: null
purge_interval: 60000
connection:
kind: Default
port: 4097
push_interval: 1000
pull_interval: 60000
r0: 3
timeout: 1000
peer_provider:
kind: K8s
selector:
c19: getting-started
namespace: default
The Config
class dynamically initializes each section using serde_yaml
and returns a configuration object which holds each section. The kind
field for every section tells serde
which layer to load.
When the run
function of the project is called, all three layers are initialized with this configuration and then started.
When implementing one of the layers, your struct might look like this:
#![allow(unused)] fn main() { /// The default state struct. #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(default)] pub struct Default { /// The default TTL (in milliseconds) to use if none is specified when setting a new value. ttl: Option<u64>, /// The interval in milliseconds in which to purge expired values. /// /// Default value is 1 minute (60000 milliseconds). purge_interval: u64, /// The DataSeeder to use for seeding the data on initialization. data_seeder: Option<Arc<RwLock<Box<dyn DataSeeder>>>>, /// The version of the current state. /// /// This is set to a random unique string on every state change. #[serde(skip_serializing, skip_deserializing)] version: Arc<RwLock<String>>, /// The SyncSender channel to use for async set operations /// /// When a set operation is being commited to the state, the state /// will pass the operation to an async handler which will then commit the /// changes to the state. #[serde(skip_serializing, skip_deserializing)] tx: Option<mpsc::SyncSender<Vec<u8>>>, /// The data storage in the form of a Key/Value hashmap. #[serde(skip_serializing, skip_deserializing)] storage: Arc<RwLock<HashMap<String, Box<Value>>>>, /// Calculating the version is a bit expensive so we use /// the dirty flag to lazily calculate the verison on-demand. #[serde(skip_serializing, skip_deserializing)] is_dirty: Arc<RwLock<bool>>, } }
If we compare this to how the Default
state configuration looks like:
state:
kind: Default
ttl: null
purge_interval: 60000
The ttl
, purge_interval
and data_seeder
fields are loaded but all other fields are skipped. This is because only the ttl
, purge_interval
and data_seeder
should be configurable while the other members of the struct are for internal use. You can mark members to be skipped with serde
's skip annotations.