Observability X – Composers

At the core of any observability instrument, which we refer to as a percept, lies a Channel or Pipe that serves as the conduit for data transfer between sources and sinks. This Pipe is responsible for transmitting data, whether it’s actively provided (pushed) by the caller of the percept or passively captured (pulled) by the percept from the environment. Percepts act as the intermediary between the application’s environment and the underlying communication channels between various consumers of perceptions, fulfilling roles such as controllers, coordinators, or cognitive agents.

The Substrates API enables the direct utilization of Pipes and Channels, while simultaneously offering a mechanism to construct percepts that adorn these fundamental elements of any sensing and synthesis pipeline. To construct a Conduit that facilitates the on-demand creation of percepts upon receiving a name, we must provide a Composer to the conduit method within the Circuit interface. Below is one of several methods in the Circuit for doing so.

Here’s the definition of the Composer interface within the Substrates API. Given a typed Channel, the compose function in the Composer interface returns a percept. This method is called by the Conduit whenever it needs to create a new percept when given a name. It does this only once per name after it’s provisioned the Channel and Pipe. In passing the Channel as opposed to just the Pipe, the Composer can access the Subject of the Channel. Percepts, for the most part, work with Pipes.

Let’s put this into practice and demonstrate how one can map the concepts of a specific problem domain, nothing just observability, to those in the Substrates API. Let’s say you’ve been asked to create a chat-like application. We’ll keep the scenario simple by only supporting direct messaging between participants. In this model, there’s the concept of a user, a person, and then a conversation, or channel. Observability is about creating models of the reality being observed, so our percept will model the person-to-person communication as a Party. A party isn’t a person but how a person within their mind represents and relates to another person. Below is an example of a Party with some prints for this article.

Within our proposed model, we have a person who participates with other persons in a conversation. If Alice and Bob have a conversation, then Alice is a person with a party that represents her communication to and from Bob, and Bob as a person has Alice as a party. A Party isn’t a person but a reference to a person from another person. Alice could talk to herself, in that case, Alice would have another Party named Alice. In the context of a general theory of systems observability, a person, which is also a Subject, serves as a Source, while a Party is the Subject of the transmission.

Below is the Composer that’ll be used by the Conduit to create a named Party. The compose method first creates a path for identifying the Party in printouts by navigating through the Channel’s Subject hierarchy using the foldTo method.

We now have developed the Party and a Composer that can create instances of Party. What is left is the person. Can you guess what concept in Substrates maps to this? Hint it needs to be a Source? It’s the Conduit, which has a source method that returns a Source from which we can subscribe. The Conduit is the person, and it’s via it we create Party instances. We’ll have a Conduit for both Alice and Bob. Let’s set things up by first creating a Circuit. and a Map for persons.

Now we just need to define a Function we can pass to the computeIfAbsent method in the Map interface.

The last setup code is creating the Subscriber that’ll route a message sent along a Pipe by a Party in one Conduit to a Party in another Conduit. The Subject of the Party is the Subject of the other Conduit. The Subject of the Conduit sending the message is the Subject of the Party in the receiving Conduit. Note the get method uses the name of a Conduit.

One last thing before we’re ready to roll is to hook up the Subscriber by wrapping around the factory and passing this new factory to the map when it encounters a key not within its set of keys. We use the tap method to shorten it.

Now let’s write some interactions to see the flow of messages between Conduits (persons) and Channels (parties).

Here’s the output to the console after executing the above code. The first part of the path is the name of the Circuit (place). The second is the name of the Conduit (person), and the third and final one is the name of the Channel (party). The first line in the console output says that Alice (Conduit) told Bob (Channel) that her name is Alice. The third line in the output says that Bob (Conduit) was told by Alice (Party) that her name is Alice.