As some of you may know, there’s a long-running discussion surrounding so-called holistic charming, as opposed to ‘charming with deltas’.
In a nutshell:
the Juju model is event-based, where each event notifies the charm of a single change in the juju model. Other parts of the state are accessible on a per-request basis by using hook tool calls. But the part of the juju model that has changed, that is what the event is about. If one writes charm logic only looking at the event at hand, we call that charming with deltas.
Sometimes it is easier to reason about charm logic if, instead of looking at the event the charm is being awakened by, we look at the ‘global’ state only. This is the holistic approach.
In reality, we typically use a combination of the two.
The problem
If you want to code holistically, that means having to fight with the Juju model in a number of ways. This has resulted in a sprawl of roll-your-own state-tracking solutions that, to use a common example, allow a charm to know whether a given relation is breaking, even outside of the context of the relation-broken hook that gave us that information in the first place.
The solution
A reductionist like me won’t be caught saying this in any other context than this, but sometimes the solution is a sprinkle of holism.
Proposal:
from charm.holism.v0.holism import Holism # a charm lib for now?
holism = Holism()
@holism
class MyCharm(ops.CharmBase):
... # your regular charm code
def some_method(self):
relation = self.some_relation()
unit = relation.units[0] # some unit
if holism.is_departing(relation, unit):
# your logic here
How it works
Holism keeps track in stored state of the ‘known’ state of all relations the charm has received events about.
Holism answers the question: “last I heard, is …”
- … this relation alive?
- … this unit joining that relation?
- … this unit departing that relation?
- … this relation being broken?
It doesn’t matter what event you’re processing, the relation data will be updated delta by delta, so you can focus on the big picture instead of keeping track of the deltas.
If the charm has received a foo-relation-created
in the past (and, so far, no foo-relation-broken
), Holism will “know” that foo
exists and is alive.
If the charm has received a foo-relation-joined
in the past for unit remote/1
(and, so far, no foo-relation-departed
for the same unit), Holism will “know” that remote/1
is joining foo
.
Cleanup
Every event, Holism will clean itself up.
For example, if Holism sees a foo-relation-broken
, for the duration of that hook, Holism will believe that the relation exists but is in a breaking
state:
holism.get_relation('foo').is_breaking == True
However, if Holism has seen a foo-relation-broken
at some point in the past (and, so far and from that point onwards, no foo-relation-created
!) and, since then, some other event (any other event) has been processed, then Holism will believe that no foo
relation is active and exists.
In other words, while we’re processing relation-broken
, the relation still exists but is in a breaking state. The next time we’re awakened (no matter the reason), we’ll assume the relation has broken down (it is no longer in a breaking state: it simply is no more).
holism.get_relation('foo') # --> RelationNotFoundError!
Onwards
- something with planned units
- other transient states other than relations?
Thoughts?
The floor is yours.