Managing Subscriptions
- 1 Introduction to Subscriptions
- 1.1 Well Known Rx
- 1.1.1 Another pico
- 1.1.2 Same pico
- 1.1 Well Known Rx
- 2 Creating a Subscription
- 3 Deleting a Subscription
- 4 Sending Messages Between Subscribers
- 4.1 Manually Sending Messages
- 4.2 Using an API Call
- 4.2.1 not yet tested
- 5 Subscription Attributes
- 6 Types of Subscriptions
- 7 Understanding the Protocol
- 7.1 Subscription Event Flow
- 7.1.1 Internal API
- 7.1.2 Subscription Creation
- 7.1.3 Subscription Deletion
- 7.1 Subscription Event Flow
Introduction to Subscriptions
To understand how subscriptions between picos work, it is important to understand what a pico channel is and what channel policies are. Inter-pico Subscriptions use pico channels to implement the subscription protocol itself (which is independent of picos).
Subscriptions allow two picos to communicate with each other, enabling the creation of peer-to-peer structures. From each pico's perspective there is a receiving channel, Rx
, and a transmitting channel, Tx
. Each pico receives events and queries from the other pico through the Rx channel and makes queries and sends events to the other pico using the Tx channel. However, it is important to note that there are only two pico channels, just a different perspective. For example, Pico A records a Tx channel and an Rx channel, but the Tx channel that pico A has recorded is actually Pico B's Rx channel and vice versa.
Subscriptions can occur between any two picos as long as the picos have a way to communicate (over HTTP or on the same engine). For example, in the Forever application, the picos belonged to different people and represented their personal contact information. The io.picolabs.subscription
ruleset is what implements subscriptions, so both picos need to have it installed.
Subscriptions are established by pico A sending a request to establish a subscription to pico B, and pico B then accepting that request.
Well Known Rx
When the io.picolabs.subscription
ruleset is installed on a pico (it is preinstalled on every pico created by Wrangler), it creates a pico channel known as the "wellKnown_Rx" channel. This channel is what receives requests to create subscriptions. The channel has a policy that only allows events related to accepting a subscription. This allows the ECI of this "wellKnown" channel to be shared publicly with anyone without fear that someone will send events that mess with the pico or send queries for information they are not allowed to have.
Although any channel which does not block the subscription events will work when creating subscriptions, it is considered good practice to use the wellKnown_Rx channel unless the subscription is between a parent and child pico. Even then, you should use the family channel to obtain the other pico’s wellKnown channel and then use its ECI to create your subscription.
A wellKnown_Rx can be accessed by querying the wellKnown_Rx()
function, shared by the io.picolabs.subscription
ruleset. It returns the channel, a Map (with keys: “id”, “tags”, “eventPolicy”, and “queryPolicy”). Of these, you will be interested only in the value of the "id"
key, the ECI.
How to obtain a pico's wellKnown channel and its ECI? Once you have a channel, say chan
, you get its ECI with chan{"id"}
. How you get the channel itself depends on whether you are looking for the well known channel of the same pico, or a different pico.
Another pico
When you are sure that it is a different pico (i.e., not the one running your ruleset), you would use the wrangler:picoQuery()
function (documented on the "Accessing a Function Shared by Another Pico" page). For example, code like this will get the ECI of the well known channel of your parent pico:
wellKnown_ECI = wrangler:picoQuery(wrangler:parent_eci(),"io.picolabs.subscription","wellKnown_Rx"){"id"}
Notice that the picoQuery
returns the channel as a Map, so you must extract its ECI using the "id"
key. Reminder that wrangler:parent_eci()
function gives you the family channel ECI for your parent.
And this is how you might get an Array of well known ECIs for all of your child picos:
wrangler:children() // an Array of Maps, each with keys: "eci", "name", and "parent_eci"
.map(function(c){
wellKnown_Rx = wrangler:picoQuery(c{"eci"},"io.picolabs.subscription","wellKnown_Rx") // the channel
c.put("wellKnown_ECI",wellKnown_Rx{"id"}) // adding a key "wellKnown_ECI" to the map for each child pico
}) // an Array of Maps, each with keys: "eci", "name", "parent_eci", and "wellKnown_ECI"
Reminder that the wrangler:children()
function gives you the family channel ECI for each of your child picos.
Same pico
Since you can’t use the wrangler:picoQuery()
function to call a function in another ruleset of the same pico, you’ll need to use a different technique. The easiest way it to look up the channel by its tags, and then extract the ECI (using the key "id"
):
wrangler:channels(["wellKnown_Rx","Tx_Rx"]) // an Array of all your channels with those tags
.head() // the first channel with those tags (and usually the only one)
{"id"} // this pico's well known ECI for subscription requests
or, without the comments:
As a reminder, a channel contains three pieces of information: the Event Channel Identifier (ECI), tags, and policies. The well known channel tags are wellKnown_Rx with Tx_Rx. This channel is used to establish new subscriptions between picos, and is meant to be publicly shared with potential subscribers.
Creating a Subscription
A wrangler:subscription
event is what initiates a subscription request. You raise it within the pico that is going to send the request to the other pico. Inside of the event you give attributes that describe the type of subscription you want to create. Any attributes added to this event will be seen by the pico receiving the request, which allow picos that receive requests to receive meta information about the requesting pico.
Requesting to Subscribe
Requesting to Subscribe
As soon as the above event is raised, Wrangler will initiate the process. If all goes well, the receiving pico will have a wrangler:inbound_pending_subscription_added
event raised within it.
Accepting a Subscribe Request
To accept (or decline) a subscription request, the receiving pico should listen for the wrangler:inbound_pending_subscription_added
event. This event will provide meta information about the type of subscription the other pico wants to form, as well as an attributes that were passed to the wrangler:subscription
event that initiated the subscription.
To accept the subscription, the client only need raise the wrangler:pending_subscription_approval
event in response to the wrangler:inbound_pending_subscription_added
event, with either the "Id"
of the soon-to-be subscription or the ECI of the Rx that this pico will receive subscription messages on. Both of these things are already attributes from the wrangler:pending_subscription_approval
event, so a simple raise wrangler event "pending_subscription_approval" attributes event:attrs;
works just fine.
Accepting a Request
Reacting to Subscription Creation or a Cancelled Request
As soon as a subscription is created, a KRL developer may want to save that subscriptions info so that they can start utilizing the subscription for communication, or immediately send a message on the subscription. The developer may react to the wrangler:subscription_added
event for this purpose. The event contains attributes that provide the new subscription's attributes and any attributes that were passed through the subscription creation event chain.
Reacting to New Subscription
Advanced Subscription Creation Example
This is an example that is closer to what safe, idiomatic code might look like for creating a relationship between picos using subscriptions. It utilizes different subscription attributes to identify and manipulate the new subscription. Assume the ruleset that these rules are written is installed on both picos.
Advanced Example
Deleting a Subscription
To delete or cancel a subscription the KRL developer raises a wrangler:subscription_cancellation
event with attributes that describe the target subscription to cancel. As a result, a wrangler:subscription_removed
event will be raised that the developer can react to.
Starting Subscription Cancellation
Reacting to a Subscription Cancellation
Sending Messages Between Subscribers
Manually Sending Messages
At any time the KRL developer can get an array of all established subscriptions by querying the established()
function in the io.picolabs.subscription
ruleset. Each member of the array will have a map giving all needed information about the subscription (descriptions of these attributes are given in the Subscription Attributes section). The "Tx"
attribute in this map is the ECI of the other pico's channel for receiving events and queries. The developer can use this value with event:send() to send events valid for the channel to the pico, or send queries using the wrangler:picoQuery()
function (see “Accessing a Function Shared by Another Pico”).
However, using the established()
function is not necessary. A developer could manage subscriptions that only they care about by saving the subscription information when a new subscription is created by reacting to the wrangler:subscription_added
event.
Attempt to Send an Event to Every Subscriber
Send a Query to a Subscriber given Subscription ID
Using an API Call
not yet tested
The rule that reacts to a wrangler:send_event_on_subs
event has not yet been tested in pico-engine version 1.0.0
Subscriptions also provides an API event that the KRL developer can raise to send an event on a subscription. The developer can choose to either send an event to a specific subscription ID, to all subscriptions with a certain Tx_role, or all subscriptions with a certain Rx_role, or a mixture of the three.
This rule sends an event to a specific subscription ID:
Send an Event to a specific Sub ID
This rule sends an event to all subscriptions that record this pico as a "node" by the Rx_role:
Send an Event to Subs with a specific Rx_role
Subscription Attributes
Subscriptions come with a few related attributes that allow for different ways of using subscriptions. Italicized attributes may be NULL.
Id
The ID of a subscription identifies a subscription relationship. By default it is a globally unique identifier assigned by wrangler, but it can be set by the developer in the initial wrangler:subscription
event. Both picos record the same ID for the subscription, as the ID represents the relationship itself.
Tx
The Tx attribute is the ECI of the other pico's Rx. It is the pico channel that this pico will send events and queries to.
Rx
The Rx attribute is the ECI that this pico receives queries and events from the other pico on. It is recorded as the Tx attribute on the other pico.
Tx_role
When creating a subscription you can store "roles" for each pico. This makes it easy to identify subscriptions with special types of relationships, such as a master-slave relationship between two picos. The Tx_role
attribute is the role that the other pico has in the subscription relationship. Hence "Transmitting channel role".
Rx_role
When creating a subscription you can store "roles" for each pico. This makes it easy to identify subscriptions with special types of relationships, such as a master-slave relationship between two picos. The Rx_role
attribute is the role that this pico has in the subscription relationship. Hence "Receiving channel role".
Tx_host
The Tx_host attribute is the network location of the engine the other pico is running on. This allows cross-engine subscriptions. If this attribute is null
then the other pico is on the same engine. When manually sending messages the developer should check if this is null and use its value to send the message if it is not null
. The event:send
action does this check for you.
Types of Subscriptions
Directional Subscription
A directional subscription is a subscription that stores roles, providing an easier way to identify relationships (such as master-slave) in a subscription.
To create a directional subscription request from pico A to pico B. Raise a wrangler:subscription
event to pico A with additional attributes, Rx_role, Tx_role. Rx_role is pico A's role in the subscription, leaving Tx_role to be pico B's role in the subscription relationship.
Cross Engine Subscription
It is easy to imagine a situation where you want to create a subscription between picos hosted on different engines. Cross engine subscriptions allow us to do so by storing the host of the other pico in a subscription relationship.
To create a cross engine subscription between pico A and pico B, raise a wrangler:subscription
event to pico A with event attribute Tx_host. Tx_host is pico B's engine host url including protocol, for example, "http://localhost:8282".
Understanding the Protocol
Subscription Event Flow
Internal API
This section is solely for illustrating how the protocol works under-the-hood. The internal events used are not static and are subject to change, and should not be selected on for long-term code.
Subscription Creation
To establish a basic subscription between pico A and pico B:
1. Pico A receives a wrangler:subscription
event which includes a wellKnown_Tx attribute. wellKnown_Tx is Pico B's wellKnown_Rx . Pico A then creates a channel for the subscription which is stored as Rx, then raises wrangler:subscription_request_needed
.
2. Pico A sends pico B wrangler:new_subscription_request
. Pico A sends all attributes it received in wrangler:subscription
with the following key attributes updated:
a. Tx - Pico A Rx.
b. name - name used for channel.
e. channel_type - string used for channel type
3. Pico B creates a Rx channel for the subscription which is stored as Rx along with Tx , Tx_verify_key, Tx_public_key in the ent:inbound
subscription entity variable.
4. After storing pending subscriptions, Pico B and Pico A raises corresponding wrangler:inbound_pending_subscription_added
event and wrangler:outbound_pending_subscription_added
event.
The state of each Picos subscription is static at this point. Pico A has an outgoing pending subscription, with relevant info stored in the ent:outbound
subscription entity variable, and Pico B has an incoming pending subscription with relevant info stored in the ent:inbound
subscription entity variable.
5. If Pico B wants to approve the subscription request, it raises the wrangler:pending_subscription_approval
event with an attribute Rx containing the Rx channel identifier of the subscription to approve. Upon successfully storing the subscription in ent:established
entity variable, pico B:
a. sends the wrangler:outbound_pending_subscription_approved
event to Pico A on Pico B's subscriptions Tx.
b. raises an API event wrangler:subscription_added.
6. Upon receiving the wrangler:outbound_pending_subscription_approved
event, Pico A adds an Tx eci to the subscription and stores the subscription in ent:established
entity variable. And raises the wrangler:subscription_added
event.
If Pico B wants to reject the subscription, it raises the wrangler:inbound_rejection
event with an attribute named Rx - which is the Rx of the subscription to reject. Pico B:
a. sends the wrangler:outbound_removal
event to Pico A on Picos B Tx ECI, which then raises an API wrangler:outbound_subscription_cancelled
event to itself.
b. raises the wrangler:inbound_removal
event, which then raises an API wrangler:inbound_subscription_cancelled
event.
If Pico A wants to revoke the subscription request, it raises the wrangler:outbound_cancellation
event with an attribute named Rx - which is the Rx of the subscription to reject. Pico A:
a. sends the wrangler:inbound_removal
event to Pico B on its wellKnown_Rx ECI, which then raises an API wrangler:inbound_subscription_cancelled
event to itself.
b. raises the wrangler:outbound_removal
event, which then raises an API wrangler:outbound_subscription_cancelled
event.
Subscription Deletion
Either pico A or pico B raises a
wrangler:subscription_cancellation e
vent. For illustration purposes we will consider it as pico A raising the event, but the process is mirrored either way.Pico A raises a
wrangler:established_removal
event within itself and sends another to pico B over the Tx channel.On reaction to established_removal the channels are deleted and the subscription removed from the list of established subscriptions.
wrangler:subscription_removed
is then raised with relevant information.
Copyright Picolabs | Licensed under Creative Commons.