Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Vostok.Configuration is a set of libraries offering configuration tools to .NET applications. It handles fetching configuration data from files or APIs, parsing and converting it to user-defined model classes.
Configuration data sources should be composable regardless of storage formats.
It should be easy to combine settings from JSON files, environment variables and custom APIs. This is achieved with configuration source concept and universal settings nodes abstraction.
"Hot" configuration updates should be easy to leverage.
Hot configuration implies handling changes in settings without application restart. This is enabled by explicitly reactive source design and settings provider methods.
Rich object models should be supported for user convenience.
Binding process supports a wide range of primitives, collections, classes, interfaces and employs a number of conventions for arbitrary types (anything with a TryParse
method works).
It should be possible to extend the library with arbitrary data sources and object models.
Two major extensions points are custom configuration sources and binders.
On-demand and subscription-based methods to obtain settings;
Support for custom settings validation;
Wide selection of built-in sources;
Composable binders;
Sources can be combined, navigated and transformed;
Integration with Microsoft configuration system;
See obtain / observe settings scenarios, caching and error handling sections for details.
Value nodes are key-value pairs with optional keys. They cannot have child nodes and thus are always the leaves of settings node trees. Standalone values are rare: most of the time value nodes can be found inside objects or arrays.
Value nodes are typically mapped to primitive types during binding.
Two value nodes are considered equal if their values match exactly and their names match up to differences in case.
Array nodes are containers used to represent sequences, such as JSON arrays. Each array node contains an ordered list of child nodes. There is no limit to nesting: arrays can contain other arrays and objects. Elements of an array are not required to have names.
Array nodes are typically mapped to ordered collections during binding.
Two array nodes are considered equal if their Children
sequences are equal elementwise and their names match up to differences in case.
A settings node is a tree with string keys and string values in its leaf nodes.
Settings nodes are immutable objects.
There are three types of nodes:
Users are not expected to implement custom node types.
Null
node instances represent absence of settings (e.g. a configuration file that does not exist).
This representation is extensively used on the rest of the pages.
Each node type implements a Merge
method that accept another node (right
in our terms) and an instance of merge options with following properties:
If both nodes are null
, the result is also null
.
If one of the nodes is null
, the non-null node wins:
left + null --> left
null + right --> right
If nodes are of different types, the right node always wins:
value + array --> array
array + object --> object
object + value --> value
If nodes are of same type, special rules apply. They are described in the next sections.
Right node always wins: left value + right value --> right value
.
Replace style (default) always preserves the right array:
left array + right array --> right array
.
Concat style produces an array containing elements from both arrays. All elements from the left array, then all elements from the second one, preserving order inside arrays.
[1, 2] + [2, 3] --> [1, 2, 2, 3]
Union style produces an array containing unique items from both arrays. The order is the same to Concat style.
[1, 2, 3] + [2, 3, 4] --> [1, 2, 3, 4]
Per element style produces an array containing items obtained by merging corresponding items (by index) from both arrays. If merged arrays have different children count, the "tail" of the longer array is preserved as-is.
[1, 2, 6] + [4, 5] --> [4, 5, 6]
Deep style (default) produces an object with union of the children from both nodes, then merges children with same names recursively.
{A:1} + {B:2} --> {A:1, B:2}
{A: {C:1}, B: {D:2}} + {A: {E:3}, B: {F:4}} --> {A: {C:1, E:3}, B: {D:2, F:4}}
Shallow style compares children of both nodes by names. If the sets of names match, regardless of order, merges the pairs of matching children recursively. Elsewise, just prefers to return the right node.
{A:1} + {B:2} --> {B:2}
{A:1} + {A:2} --> {A:2}
are containers used to represent objects with named fields/properties. Each object node contains a map of child nodes with their names as keys. There is no limit to nesting: objects can contain other objects and arrays. Elements of an object are required to have non-null names.
Object nodes are typically mapped to arbitrary classes and structs during .
Settings nodes are the intermediate representation of configuration data and one of the core concepts of the library. Their main purpose is to abstract away various configuration formats (JSON, XML, etc.) so that can be implemented once without regard to the nature of being used.
produce settings nodes as their primary artifacts.
is the process of converting a settings node to an arbitrary .NET object.
Settings nodes are implemented in the .
Despite being a somewhat internal API used directly only in a handful of advanced scenarios, settings nodes are crucial for a solid understanding of how configuration data to objects in C# code via different node types and .
Implementation of a ;
Implementation of a ;
of configuration sources
, used to hold data;
, used to represent sequences;
, used to represent objects with named properties.
All node types implement the interface and have following properties:
All nodes also implement a operation.
All node types implement a JSON-like ToString()
method. Its result may look like this for a sample with two nested :
Scoping is the operation of navigating a tree by accessing child nodes of via names in a case-insensitive manner. A sequence of names resembling a path in the object structure, such as ["property1", "property2"]
is called a scope.
Scoping does not work on and nodes (always results in null
).
Scoping is used to map object fields/properties to nodes of settings tree during . It also allows to — create a source that returns a subtree of settings returned by the original source.
Merge is the operation of reducing two to one: left + right --> result
.
Its primary use is to .
is a handy public helper used to merge arbitrary nodes that handles nulls:
Property
Description
Name
Required if nested in an object node, optional otherwise.
Value
Useful payload. The value of a object field or array element. Can be null.
Children
Always returns an empty sequence.
this[name]
Always returns null
.
roperty
Description
Name
Required if nested in an object node, optional otherwise.
Value
Always returns null
. Only value nodes can have values.
Children
Returns an ordered sequence of child nodes.
ChildrenCount
Returns the number of elements in the Children
sequence.
this[name]
Always returns null
. Arrays cannot be navigated with scoping.
roperty | Description |
|
|
| Returns an unordered sequence of child nodes. |
| Returns the number of elements in the |
| Returns a child node with given name or |
Property | Type | Description |
|
| Node name. Case-insensitive. |
|
|
|
|
|
|
Property | Values | Description |
|
|
|
|
Configuration sources fetch data from storage (local files or remote APIs) and convert it to settings nodes, abstracting away actual data formats such as JSON or YAML.
They are not meant to be consumed directly and should be used in conjunction with a configuration provider (see assign sources to types and obtain settings from provider scenarios).
Sources are also responsible for data change detection. They expose a reactive interface with subscription support:
On each update, triggered either periodically or by an internal event, the source emits a pair: (settings, null)
on success or (null, error)
on failure. It's not required to deduplicate settings or errors at this level, although it's never wrong to do so.
Sources must never block indefinitely while waiting for data and should rather publish null
settings after a short initial timeout.
Sources must be thread-safe and should be designed to support multiple concurrent observers. It is also expected that every new observer would immediately receive a notification with current state upon subscription.
Here are some of the often used source implementations:
It's also possible to implement a custom source.
Required if nested in an , optional otherwise.
Always returns null
. Only can have values.
Node value. Only present in .
Sequence of child nodes in containers: and .
Name-based indexer used to navigate (see ).
Type of the merge procedure performed on . Deep
is the default style.
Type of the merge procedure performed on . Replace
is the default style.
This page describes how configuration providers deal with errors arising from sources and binders.
It can be summarized in two simple rules:
If an error occurs and no correct settings instance has been observed for the requested type thus far, the error is propagated to the calling code, resulting in exceptions from Get method. A subsequent settings update with correct data automatically "heals" future Get calls.
If an error occurs and a correct settings instance has already been observed for the requested type at least once, the error is reported in background and does not cause Get method to fail: last seen correct instance is returned from cache instead.
The second guarantee can be violated by cache overflow events. Read the caching section to find out how to avoid them.
Observe method never produces OnError
or OnCompleted
notifications. It only reports successful settings updates. The errors are handled in background and can be logged.
Configuration providers cache bound settings for each (type, source)
pair where sources are compared by reference. Caching ensures a solid performance level: only the first Get call is somewhat expensive while all the subsequent ones are extremely cheap. The cache is automatically updated when the underlying source issues new data.
Due to caching, configuration provider instances should be reused as much as possible. Ideally there should be just one singleton instance in the application.
Special care should be taken when using Get and Observe methods with short-lived source instances passed on per-call basis. This could cause poor performance due to cache misses and even lead to cache overflow events. Overflow events may cause violations of error handling guarantees. Default cache capacity is 50 but can be tuned in provider settings:
This pitfall is easy to fall into as all of the source-related extensions (combine, scope, transform, etc) return decorators that are treated as distinct sources. The only viable solution is to cache these derivative sources.
This library contains an implementation of configuration provider, binding, and a couple of custom configuration primitives (such as DataSize and DataRate).
GitHub repository: vostok/configuration
NuGet package: Vostok.Configuration
Cement users should reference this module with the following command:
This library contains a few basic implementations of , such as , , , , . It also hosts a set of extensions to manipulate sources (, , , , , ) and helpers aiding in .
GitHub repository:
NuGet package:
users should reference this module with the following command:
This library contains a source that fetches settings from ClusterConfig. It also allows to apply custom parsers in order to support formats other than CC's native one.
GitHub repository: vostok/configuration.sources.cc
NuGet package: Vostok.Configuration.Sources.ClusterConfig
Cement users should reference this module with the following command:
This library contains a source implementation based on HashiCorp Vault secrets.
GitHub repository: vostok/configuration.sources.vault
NuGet package: Vostok.Configuration.Sources.Vault
Cement users should reference this module with the following command:
Location: main sources module.
A constant source returns a preconfigured settings node and never issues any updates.
A lazy constant source does the same but defers the node acquisition until first subscription:
Errors in the provided delegate are propagated via (null, error)
notification. It's guaranteed to execute not more than once.
Constant sources are handy for unit testing and may serve as base classes for custom sources.
Location: Sources.Xml module.
XmlStringSource parses well-formed XML documents from in-memory strings and supports manual external updates:
XmlFileSource parses XML files and automatically watches for changes:
A file that does not exist simply leads to a null
node, which implies default settings values unless something is explicitly required.
Location: main sources module.
EnvironmentVariablesSource converts process environment variables into settings nodes.
Keys with dots, colons and double underscores are treated as hierarchical and get split into segments:
This source is static and never issues data updates.
Location: main sources module.
ObjectSource converts an arbitrary object (including anonymous types) to a settings node:
Conversion process obeys the following priority list:
Objects with overridden ToString
are converted to value nodes;
Dictionaries with primitive key types are converted to object nodes;
Conversion is performed recursively for keys and values;
Objects that implement IEnumerable
are converted to array nodes;
Conversion is performed recursively for sequence elements;
Everything else is converted to object nodes;
Conversion is performed recursively for public instance fields and properties;
Like constant sources, object source does not produce any updates by itself; conversion exceptions are exposed via (null, error)
notifications.
Location: .
parses well-formed YAML documents from in-memory strings and supports manual external updates:
parses YAML files and automatically watches for changes:
A file that does not exist simply leads to a null
, which implies default settings values unless something is .
This source is not documented yet. Come back for updates later.
Location: main sources module.
CommandLineSource converts CLI arguments into settings nodes.
It supports 7 syntax options for key-value parameters:
--key=value
--key value
-key=value
-key value
/key=value
/key value
key=value
Keys with dots (such as a.b.c
) are treated as hierarchical and get split into segments:
Multiple occurrences of the same key are merged into arrays.
This source is static and never issues data updates.
Standalone keys may optionally be supplied with a default value.
Standalone values may optionally be grouped under default key.
Binders convert settings nodes to objects. There are built-in binders for primitives, collections, classes and structs. One can also extend the library by implementing a custom binder.
Requires: main module.
When using configuration providers, Get and Observe method overloads without parameters require an explicit association of the model type with a configuration source.
A source can only be assigned to a type before any Get or Observe calls are made for that type:
Requires: main module.
Settings can be obtained on-demand with configuration provider's Get method.
With prior assignment of sources:
With sources passed on per-call basis:
Always returns the most recent version of settings (updates may happen in background):
Call Get on every settings access for "hot" configuration;
Call Get once and cache the result for "cold" configuration;
First call for a type may block or throw exceptions due to source latency, data unavailability or incorrect data format. This behavior persists until a data update remedies the error;
Once warmed up, subsequent calls never block or throw errors and are extremely cheap due to caching. Future errors are not propagated to the calling code, but can be logged. Get calls return the last seen correct settings object;
Requires: main module.
One can subscribe to settings updates for a type with configuration provider's Observe method.
With prior assignment of sources:
With sources passed on per-call basis:
Temporary subscriptions should be disposed of as they might hold on to resources in sources:
OnError
and OnCompleted
notifications are never produced. Only successful settings updates are propagated to the observers. This means that there will be no notifications if initial attempt to provide settings fails with an error and no further data updates are published by the source.
The only way to notice errors when using Observe is to enable error logging.
The subscription is not guaranteed to immediately produce a notification. The first notification may be delayed due to data not having been fetched from the source yet. However, once a valid settings instance has been observed, all new observers receive a notification with current actual settings upon subscription. This notification is published on a background thread, so don't count on it being delivered after Subscribe
call completes.
It's recommended to obtain initial settings instance with Get method before subscribing to updates with Observe. This practice ensures correct error propagation, warms up the cache and eliminates situations where the settings are not ready yet on access attempt.
Requires: abstractions module.
By default settings are subject to logging, which is generally not appropriate for secrets. This can be fixed either by disabling logging entirely on the configuration provider or by annotating fields and properties of the model with [Secret]
attribute:
Requires: abstractions module.
Name aliases provide alternative keys to look for in settings nodes when performing binding.
They can be applied to fields and properties with a special attribute:
It's allowed to assign multiple aliases to a single member:
Aliases do not handle ambiguity. If the source returns data with more than one of the lookup keys assigned to a field or property, binding fails with an error.
Requires: abstractions module.
By default all settings are optional; that is, absence of relevant data in the source results in default values during binding and does not produce errors.
Some parts of configuration may be crucial to the application to the point that it's pointless to start without them being initialized. These fields and properties should be annotated with [Required]
attribute:
Default behavior can be inverted in the scope of a type with [RequiredByDefault]
attribute. Individual fields and properties can then be made optional with [Optional]
attribute:
Requires: sources module.
This operation allows to effectively disable data updates on a source:
Requires: main module.
The basic recommended way to get most up-to-date settings in the presence of background updates is to use provider's Get method (see the relevant scenario) on each access attempt. However, it's also possible to obtain an inherently dynamic settings object whose properties are updated under the hood. This requires to use an interface as the settings model:
Note that in order to get a guaranteed consistent view of the settings without "tearing" (observing a mix of values from before and after update due to a race condition) when using a nested object (section), it's recommended to access a snapshot of this nested object saved in a variable:
Requires: main module.
Configuration provider class has a property named Default
that serves as a global static source of IConfigurationProvider
instance.
It's intended use case is self-sufficient configuration in libraries: library authors may not want to force their users to provide an IConfigurationProvider
instance each time they're using library classes. Instead, these classes could just obtain a log from the shared property:
By default this property returns a singleton provider with default settings. It can be configured explicitly in the application:
Requires: sources module.
Value substitution feature allows to replace placeholders in settings data. It's based on the transformation extensions and uses #{}
syntax for placeholders.
Requires: sources module.
This operation allows to modify source contents by applying a custom delegate. Most often this involves rewriting values stored in value nodes:
Requires: sources module.
This operation allows to embed source's data into a nested object section:
Requires: abstractions module, main module (constraints).
Validation feature allows to associate a custom user-made validator with a settings type. Validation occurs during binding and results in binding errors for incorrect settings.
There are also built-in validation constraints you can use to create a custom validator. Just inherit your validator class from ConstraintsValidator
and override a method returning constraints to be checked:
Here's a list of all currently implemented constraint types:
NotNullConstraint
for arbitrary reference types;
NotNullOrEmptyConstraint
for strings;
NotNullOrWhitespaceConstraint
for strings;
RangeConstraint
, LessConstraint
, LessOrEqualConstraint
, GreaterConstraint
and GreaterOrEqualConstraint
for any types that implement IComparable
;
UniqueConstraint
to check that a set of field/properties only contains unique values;
Requires: sources module.
Sources module offers a couple of base classes to assist in implementing custom configuration sources.
For in-memory sources or sources that can update an in-memory one from a remote API.
For sources based on local files. Includes automatic reload on file updates.
Requires: abstractions module.
This extension point covers scenarios where existing binders are not sufficient to handle required types.
Custom binders should be used as a last resort when constructor injection and parsing conventions are not powerful enough.
Creating a binder for a type is just a matter of implementing an interface:
Custom binders can be applied to specific fields and properties or whole types:
Requires: abstractions module, main module.
Here's the simplest way to experience Vostok.Configuration for the first time:
Install main, abstractions and sources.json modules:
Define a settings model class:
Create a JSON file with settings:
Obtain a model instance with a configuration provider from a file source and print it:
Great job! Here are some possible next steps:
Learn the most basic concepts: settings nodes, sources, binding, provider;
Explore available modules and source implementations;
Go over the basic scenarios section.
Binding is the process of initializing a model (an instance of almost arbitrary type) with data from a settings tree obtained from a configuration source:
node {A: 1, B: 2} --> new CustomModel { A = 1, B = 2}
The resulting model is queried by the application code with a configuration provider to access settings.
Binding is implemented by a set of binders, each of which knows how to convert a settings node to an object of a specific type.
Binders are composable: if there's a registered binder for Dictionary<T1, T2>
, string
and int
, then it's possible to bind to Dictionary<string, int>
. This is heavily used for collections.
A typical binding process starts with a class binder and proceeds downward by matching fields and properties with scoped node subtrees by names and invoking appropriate binders:
See all binders descriptions to learn more about this process.
Binding fails if there's at least one error on any level. Errors may arise from incorrect value formats for primitives, missing values for required fields and properties, mismatches of settings node types or missing binders for requested types.
In case of failure, a complete list of all errors is presented in resulting exception.
Configuration provider is responsible for the binding process and offers methods to obtain final settings models. It's also responsible for caching and error handling. Providers are used directly by the application code to either obtain settings on demand or subscribe to updates.
Fetches the newest version of settings of given type:
Allows to subscribe for updates of settings of given type:
Both Get and Observe methods have 2 variations:
The one without any parameters requires a prior assignment of a source to the requested type;
This library contains core interfaces (IConfigurationSource, IConfigurationProvider), intermediate data model (settings nodes) and attributes used to annotate user models, such as RequiredAttribute.
GitHub repository: vostok/configuration.abstractions
NuGet package: Vostok.Configuration.Abstractions
Cement users should reference this module with the following command:
This library contains XML-based configuration source implementations: file and string. It also exposes a configuration parser for integration with other sources, such as ClusterConfig.
GitHub repository: vostok/configuration.sources.xml
NuGet package: Vostok.Configuration.Sources.Xml
Cement users should reference this module with the following command:
This library provides an integration with Vostok logging, allowing to log errors and settings updates.
GitHub repository: vostok/configuration.logging
NuGet package: Vostok.Configuration.Logging
Cement users should reference this module with the following command:
This library contains JSON-based configuration source implementations: file and string. It also exposes a configuration parser for integration with other sources, such as ClusterConfig.
GitHub repository: vostok/configuration.sources.json
NuGet package: Vostok.Configuration.Sources.Json
Cement users should reference this module with the following command:
This library provides a one-way integration with Microsoft configuration extensions, allowing to inject Vostok sources as MS configuration providers.
GitHub repository: vostok/configuration.microsoft
NuGet package: Vostok.Configuration.Microsoft
Cement users should reference this module with the following command:
Location: Sources.Json module.
JsonStringSource parses well-formed JSON documents from in-memory strings and supports manual external updates:
​JsonFileSource parses JSON files and automatically watches for changes:
A file that does not exist simply leads to a null
node, which implies default settings values unless something is explicitly required.
This library contains YAML-based configuration source implementations: file and string. It also exposes a configuration parser for integration with other sources, such as ClusterConfig.
GitHub repository: vostok/configuration.sources.yaml
NuGet package: Vostok.Configuration.Sources.Yaml
Cement users should reference this module with the following command:
This source is not documented yet. Come back for updates later.
Primitive types are parsed from string values in value nodes.
DataSize and DataRate are custom new types. They also provide factory extensions and operators:
There's also support for types that implement Parse
or TryParse
method with standard signature:
This allows to use arbitrary types with string representation without resorting to implementation of custom binders.
Value node or a container (array/object) node with a single value node child.
Default value for the type is used unless explictly required.
Explicitly specified null
string value has the same effect.
Parsing errors arising from incorrect value formats lead to binder failure and result in exceptions, even for optional members (see classes and structs for more context).
Type
Allowed format examples
string
Literally any string.
char
Literally any single character.
bool
true
, True
, TRUE
byte, sbyte
Anything built-in TryParse
method can grok.
short, ushort
Anything built-in TryParse
method can grok.
int, uint
Anything built-in TryParse
method can grok.
long, ulong
Anything built-in TryParse
method can grok.
float, double, decimal
1.23
, 1,23
, 5,12e2
Guid
Anything built-in TryParse
method can grok.
Uri
http://example.com
, example.com/some
, /part/of/path
TimeSpan
00:12:34
, 2 seconds
, 500 ms
, 1.5 days
, 10s
, 0.5 minutes
DateTime(Offset)
2018-03-14 15:09:26.535
, 20050809T181142+0330
IPAddress
127.0.0.1
, 2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d
IPEndPoint
192.168.1.10:80
453453
(just bytes), 1 kb
, 24.3 megabytes
, 500 TB
500
(just bytes/sec), 200 kilobytes/second
, 5 GB/sec
, 80 mb/s
Encoding
utf-8
, us-ascii
Enum
types
Anything built-in TryParse
method can grok.
Nullable structs
A valid value or null
.
Arrays and lists:
T[]
List<T>
IEnumerable<T>
(backed by T[]
)
IReadOnlyList<T>
(backed by T[]
)
IReadOnlyCollection<T>
(backed by T[]
)
ICollection<T>
(backed by List<T>
)
IList<T>
(backed by List<T>
)
Dictionaries:
Dictionary<TKey, TValue>
IDictionary<TKey, TValue>
(backed by Dictionary
)
IReadOnlyDictionary<TKey, TValue>
(backed by Dictionary
)
Sets:
HashSet<T>
ISet<T>
(backed by HashSet
)
Collections require binders defined for element types (both keys and values in case of dictionaries).
Nulls are not valid as dictionary keys.
Any element binding failure results in complete collection binding failure.
Settings models can be printed in JSON or YAML formats to generate configuration file examples.
Fields and properties don't have to be mutable: readonly fields and properties with private setters (or even without one) are fine.
Following members are ignored: indexers, constants, static and non-public fields and properties, computed properties without backing fields.
Binders are required for all public fields and properties.
Collections can have and other collections as elements.
or node. Empty nodes are converted to empty collections.
An empty collection is returned unless .
A custom element comparer (such as StringComparer.OrdinalIgnoreCase
for case-insensitive dictionary keys) can be achieved by wrapping the collection in a custom type and utilizing the .
A type that has exactly one constructor with a single argument of a type that can be bound can also be bound with argument injection. This is especially useful for customization:
Requires: .
The binder starts off by creating an instance of the bound type with default values of public fields and properties (the same way as Activator.CreateInstance
does) and then recursively binds the value of every field and property to a corresponding subsection of the settings tree. See page for a step-by-step illustration of this process.
Nested classes and are supported without restrictions.
When binding a field or property value, the settings tree is to the member name. This imposes a requirement to synchronize naming in configuration data provided by and models in the application code. In order to bind a model property named Timeout
from a section of JSON file, this section must contain a property with the same name (barring case).
allow to decouple property names from configuration data names.
By default all fields and properties are treated as optional: they are just left with default values if no data could be found in the settings tree (default values may come from field and property initializers). It's possible to though. Required members with no data in the settings tree cause the binding process to fail and produce an exception.
Model classes must either have a parameterless constructor, a constructor with a single parameter, or have an OmitConstructorsAttribute
(which allows , therefore, no default parameters are present).
Only are supported.
Requires: logging module.
See logging documentation for the log interface required here.
Secret settings should be marked with an attribute to avoid being logged.
Requires: .
See for cases where this logging is useful.
See for the log interface required here.
Secret settings should be to avoid being logged.
Requires: main sources module.
A source can be scoped just like settings nodes can. Resulting source's data is exactly base source's data scoped to given path:
Requires: main sources module.
Multiple configuration sources can be combined into a single composite source whose data is produced by merging the settings trees provided by original sources.
Order of the sources is important: settings from sources that come later in the list have greater priority, hence the rightmost source should be the most specific/significant. In other words, merging is performed in a left-to-right fashion.
Updates are pushed to subscribers each time one of the component sources generates new settings.