Introduction
Work is under-way to implement model generations via “branches”. Branches are a mechanism by which changes to charm URL, charm configuration and resources can be made, then applied selectively to application units by having them track the branch. When the changes are assessed and deemed appropriate, the branch is then “committed” to the model whereupon the changes are applied throughout, affecting all units as normal.
This post deals with charm configuration only. Charm URL and resources will be considered at a later date.
It is intended to work toward answering the following questions.
- In what form should generational configuration deltas be written to state?
- How should a unit determine (and request configuration for) a generational branch that it is tracking?
- How should the multiwatcher notify of changes to generational configuration in addition to base configuration?
- How should historical configuration changes be recorded when a branch is committed to the model?
In what form should generational configuration deltas be written to state?
Charm configuration is stored as deltas - only values set by the operator are stored in settings. When charm configuration is read, the charm configuration defaults are retrieved, then the deltas from settings are applied, before being returned to the user.
The first implementation, currently seen on the develop branch, is to use a new settings document suffixed with the branch identifier. This document is created from the existing settings for charm configuration upon the first read or write of settings for the branch/application. It is worth noting that this does not track changes made under the branch - it starts as a snapshot of all non-default values.
Other options might be:
- Keep storing in settings, but only generational deltas, meaning that for branches, configuration is defaults + settings + branch deltas.
- Store generational configuration in the generations collection against its branch.
- Create a new collection for generational configuration altogether.
How should a unit determine a generational branch that it is tracking?
When a unit is set to track a branch, it obviously needs to be made aware of this fact so that it can read (and watch for changes to) its charm configuration.
On the develop branch, applications with configuration changes and any tracking units are recorded against the branch in the generations collection.
Under this scheme every unit would have to watch the generation collection and interrogate all “in flight” branches to see if it has been set to track any of them. Any triggered watch would result in the unit re-requesting and watching a new location for configuration.
One alternative might be record tracked branches against unit documents directly, so that this search overhead was ameliorated. If it was only recorded against the unit, interrogating a branch would require all unit documents to be read. If it was stored in both places, care would be required to ensure that the two sources of data are always consistent.
How should the multiwatcher notify of changes to generational configuration?
This remains an open question - no logic has been written to handle this.
At the same time as the generation feature is implemented, units will be changed to retrieve and watch configuration via the new model cache. The cache can be thought of as a watch multiplexer. It maintains a cache of all model entities whose types it is set to track (applications/units/machines etc) by using a multiwatcher, and entities themselves watch the cache for change notifications instead of using state-based watchers.
Wherever generational configuration is stored, the multiwatcher will need to also watch those documents in order to populate the model cache. This would tend to favour storing generational configuration in the settings collection, as a sightly modified version of the standard settings watcher would be easy to implement.
Units watching the model cache for charm configuration obviously need to supply any tracking branch as an argument.
How should historical configuration changes be recorded when a branch is committed?
As for the multiwatcher, no logic exists for this yet.
The obvious way of doing this is to ensure the configuration changes are in the generations collection against the branch at commit time. This would be implicit if we store generational configuration there all the time, or we could copy from another location upon commit.