As work on Juju 4.0 (sans MongoDB) continues apace, we’ve been trickling pieces of our emerging relational data model into this post.
We’ll now look at some architectural aspects of how we’re supplying access to this data model via services.
As a starting point I’ll explain the following arrangement.
Controller charm
In major versions 2 and 3 of Juju, we allowed the isolation of traffic across the MongoDB cluster by setting a space name in the juju-ha-space
controller configuration article.
This will be dropped in 4.0 in favour of using a binding for the controller charm’s dbcluster
peer relation. In this way we remove an item of esoteria and replace it with a well-known concept from the Juju ecosystem.
The controller charm maintains a configuration file on disk similar to agent configuration. Its contents include the desired cluster topology.
Database accessor
The dbWorker
is responsible for starting the local Dqlite node and negotiating its membership in the cluster. Each controller is a Dqlite node. Nodes are joined to the cluster based on the configuration file written by the controller charm.
It also maintains a set of trackedDB
workers, one for each database. Juju has a main controller database, plus one for each model. These workers export to other workers, the ability to acquire a transaction runner. Each one monitors the health of its database connection and implements a retry strategy for transactions submitted the database.
Change stream
Juju’s operation is heavily predicated on watchers. Watchers allow agents to watch some aspect of the data model and receive a notification if it changes. For example, the Juju provisioner watches for new machine records and sets about provisioning them when it is notified.
In Juju 3.x versions we read a stream of changes emitted by MongoDB, and multiplex this to individual watchers based on the particular events that they subscribe to.
Dqlite does not possess such a mechanism, so the Juju team designed a trigger-based change log, which is read in a loop by the changeStreamWorker
. This worker dispatches terms to another worker that manages subscriptions from watchers.
By wrapping the transaction runner ability supplied by the trackedDB
workers in another worker that allows plugging in to an event stream, we can export the ability to both run transactions and create watchers.
Domain services
The ultimate consumers of runner/watcher capability are the main Juju domain services. This is where we implement all of our application logic. These services are recruited by the API server, which services clients and agents, and by other workers in the controller database.
Simple… not so fast
In the next post we’ll go into more detail on how we provide access to the cloud substrates (providers) from the domain services, which will shed some light on the extent to which we’ve abbreviated the diagram above.