It seems that a frequent point of confusion in charming is reconciling between holistic vs deltas approaches.
- Relation events give us deltas: unit/x joined or unit/x departed, and now we need to append/pop a section to/from an existing config file.
- Idempotentency and robustness call for holistic: for example, on config-changed after upgrade we need to iterate over all relations to construct a full config from scratch.
Some juju events are broadly scoped:
- upgrade-charm (no relation events fire so we need to rebuild the files manifest from scratch)
- config-changed (we don’t know which one changed or what the previous config has been)
Some juju events are narrowly scoped:
- pebble-ready for container x (now we can push the manifest and add the layer to container x)
- unit x departed from relation y (now we need to pop unit x from a config file)
But even the seemingly narrowly scoped events may have broad implications:
- Pebble-ready means we are ready to add a pebble layer for the first time, but we need to construct it from several relations (if they exist). Since this is the first time we’re doing this, we need to iterate over all the relevant relations, etc. (I guess pebble-ready could have been considered a narrowly-scoped event if relation events were held off until after pebble-ready.)
- A departing tls-certificates relation may result in modifying multiple sections of a config file and replanning the layer to revert the workload back to HTTP instead of HTTPS, as well as update dependent libs with the change of endpoint scheme.
Charming with deltas
Perhaps we would be able to better stick to the deltas approach if we had some new juju events at our disposal.
Still, it seems to fall short when faced with inter-lib dependencies: how useful is a tls-certificates-departed event, if a dependent library was already instantiated in MyCharm’s constructor without taking into account the departing unit?
On upgrade, no relation events are emitted (unless you modify relation data as part of the upgrade sequence). So if, for example, you get certificates over relation data, they would all be lost after an upgrade. And you do not want persistent storage because then you would need to figure out which ones you need to delete after an upgrade.
So on upgrade you’d need to iterate over all relations and write those certs to disk. But not quite on upgrade-charm. We need the container to be there, so this must happen of pebble-ready (the path is short from here to common exit hook and manifest driven charming).
“Holistically” may mean “take everything into consideration, all the time”.
Inevitably this introduces a common-hook/reconciler/funnel pattern. This already seems to be practiced widely.
To further improve the holistic charming experience, it seems that “relation-departed” needs to be followed by “relation-changed”, where no stale data from the departing unit is present (juju/2026302).
At ~observability we often opt for the holistic approach. How about you?