Learning Objectives
After completing this lesson, you will be able to do the following:
- Explain what a pico subscription is
- Understand pico-to-pico relationships based on subscriptions
- Use the developer tools to manage pico subscriptions
- Programmatically link picos using a subscription and have them interact.
Motivation
In the previous lesson, you learned about creating pico parent-child relationships. However, in distributed systems, not everything is a parent-child relationship. In fact, there are many systems which are based off of peer-to-peer relationships. Each node in a peer-to-peer system needs some way to communicate with a different node independent of a parent child hierarchy. For example, a flower store may wish to create a relationship with a delivery driver, so that the store can broadcast a new delivery request, and the driver can bid on delivering that request. This is where subscriptions come in. Subscriptions are a relationship link from one pico to another completely independent of the parent-child hierarchy. This lesson will show you how to create these subscriptions (relationships) among picos so that they may communicate directly with each other rather than through some other median.
Prerequisites
You should have already completed the following lessons:
Pico Engine Quickstart lesson. (Be able to register and install rulesets)
Events and Queries Lesson. (Be able to create channels and understand how events are raised on channels)
- Pico State Lesson. (Be able to store and retrieve state using entity variables, both simple and complex)
Pico-Based Systems Lesson. (Be able to manage the pico lifecycle using UI and programmatically)
- Read Wrangler Subscriptions description.
Contents
Video Tutorial
Subscriptions
A subscription represents a relationship between two picos. It has a name and two channels, one from each pico participating in the subscription to the other. Each side in the subscription can be given a role to further identify the purpose of the subscription.
Managing Subscriptions Programmatically
Wrangler provides functions, actions, and rules for managing subscriptions from within your KRL code.
In this section, we'll use a simple example. Here the owner pico has three child picos, as shown by the familiar black parent-child edges. However, let's say the "Mischief Pico" needs to communicate with the other two picos.
The subscriptions which we will create to enable this pico to pico communication are shown as dashed magenta lines. (This diagram is available at localhost:8080/?subscr )
Prepare for this section by manually creating three child picos of your root pico.
- Install this ruleset into your root pico: https://raw.githubusercontent.com/Picolab/pico_lessons/master/subscriptions/mischief.owner.krl,
- Install this ruleset into your Mischief pico: https://raw.githubusercontent.com/Picolab/pico_lessons/master/subscriptions/mischief.krl, and
- Finally, install this ruleset into each of your thing picos: https://raw.githubusercontent.com/Picolab/pico_lessons/master/subscriptions/mischief.thing.krl
Initiating subscriptions by introduction
It is possible to initiate a subscription with a rule running in a pico which already has in its possession the ECI's of the two picos which need to be related by the subscription. Once you have set up the three child picos, Mischief, Thing1, and Thing2, and installed their respective rulesets, study the code and prepare for the introductions.
The root pico already has all three of the other ECI's in its array of children (maintained by the io.picolabs.wrangler aka Wrangler ruleset), but it doesn't know which is which. So we added this rule to allow it to distinguish between the Mischief pico (which identifies itself by ECI) and the Thing1 and Thing2 picos.
rule mischief_who { select when mischief who pre { mischief = event:attr("eci") things = wrangler:children().map(function(v){v{"eci"}}) .filter(function(v){v != mischief}) } always { ent:mischief := mischief; ent:things := things } }
The ruleset for the Mischief pico sets up a button "mischief/identity" in that pico's Testing tab which will send this event to the owner pico. Navigate to the Testing tab of the Mischief pico and click the button. Doing so will send the event mischief:who
to the owner pico, and that event will trigger the rule shown above. You can check the result of this calculation by looking at the entity variables held by the mischief.owner
ruleset in the Rulesets tab of the Owner Pico.
Once the root pico has all of the ECI's, the code to introduce the Mischief pico to each of the thing picos is straight-forward.
rule mischief_subscriptions { select when mischief subscriptions foreach ent:things setting(thing,index) // introduce mischief pico to thing[index] pico event:send( { "eci": ent:mischief, "eid": "subscription", "domain": "wrangler", "type": "subscription", "attrs": { "name": "thing" + (index.as("Number")+1), "Rx_role": "controller", "Tx_role": "thing", "channel_type": "subscription", "wellKnown_Tx": thing } } ) }
The mischief.owner
ruleset also sets up a button in the root pico's Testing tab, "mischief/subscriptions" to send the event that causes this rule to evaluate. Make sure to install io.picolabs.subscription
on all of your picos, otherwise none of the rulesets we provided will select on these events. Navigate to the Testing tab for the Root Pico to click on this button, which will cause the mischief_subscriptions
rule to evaluate and make the subscription requests to all of the thing picos.
The final step is that a rule selecting on wrangler/inbound_pending_subscription_added
is in each of the thing picos (the ruleset mischief.thing
), and this will complete the subscription process. The rule looks like this:
rule auto_accept { select when wrangler inbound_pending_subscription_added fired { raise wrangler event "pending_subscription_approval" attributes event:attrs } }
You can see the results by refreshing the UI and navigating to the Subscriptions tab for any of the picos involved in one or both of the two subscriptions.
Initiating subscriptions by direct request
A pico can directly initiate a subscription. The subscription is initiated by a rule installed in the pico raising
the wrangler:subscription
event in the postlude block of one of its rules.
raise wrangler event "subscription" attributes { "name" : "thing1", "Rx_role": "thing", "Tx_role": "controller", "channel_type": "subscription", "wellKnown_Tx" : thing1 }
As in the case of introductions, the target pico must have a rule selecting on wrangler/inbound_pending_subscription_added
which will complete the subscription process by raising the wrangler/
pending_subscription_approval
event.
Writing rules that use subscriptions
Suppose that we wanted the Mischief pico to respond to a mischief/hat_lifted
event and send the same event to all of the thing picos that it controls. Assuming that you have arranged to use Subscriptions as a module in the meta block, as in
use module io.picolabs.subscription alias Subscriptions
a rule like this one would accomplish this:
rule mischief_hat_lifted { select when mischief hat_lifted foreach Subscriptions:established("Tx_role","thing") setting (subscription) pre { thing_subs = subscription.klog("subs") } event:send( { "eci": subscription{"Tx"}, "eid": "hat-lifted", "domain": "mischief", "type": "hat_lifted" } ) }
Notice that the rule evaluates for each of the subscriptions for which the receiver role is "thing". In each case, it is able to reach that pico through Tx
which is the event channel identifier (ECI) established when the subscription was accepted.
Deleting subscriptions
Engine Compatibility
Eventually, Wrangler will fully integrate subscriptions, so that when you delete a child pico any subscriptions it participates in will also be deleted. In the meantime, you may wish to programmatically delete such subscriptions before requesting the deletion of the child pico.
To delete a subscription, either of the picos which participate in the subscription would raise
the event wrangler/subscription_cancellation
including an attribute named "Tx" whose value is the ECI of the other pico in the subscription. For example, if one of the thing picos wanted to cancel its subscription, it might include the code shown in this rule fragment:
pre { // find the first of my subscriptions in which I play the "thing" role sub_to_delete = Subscriptions:established("Rx_role","thing").first(); } if sub_to_delete then noop(); fired { raise wrangler event "subscription_cancellation" attributes {"Tx":sub_to_delete{"Tx"}} }
The raised event triggers a rule in the io.picolabs.subscription
ruleset in this pico. This rule takes care of deleting both sides of the subscription.
Managing Subscriptions Manually
We showed above how to write KRL code to initiate a subscription. Here, we will create a subscription manually (using a RESTful interface such as the Postman REST client, or curl
, or the browser–in this example we'll use the browser).
Prepare a URL like this one in your browser (the line breaks shown here are for readability–your URL will not contain line breaks):
http://localhost:8080/sky/event/ME9i5bo26xFPEUp9yBHUbq/subscr/wrangler/subscription? wellKnown_Tx=GTrQY4dzrkr7n39Lezp9mf& Rx_role=controller& Tx_role=thing& name=thing1& channel_type=subscription
Your URL will be different because the ECI to which the event is sent (in the example, ME9i5bo26xFPEUp9yBHUbq
) needs to be replaced by the main ECI of your Mischief Pico, and the wellKnown_Tx
parameter needs to indicate the ECI of your Thing1 Pico.
A new channel is created in each of the two picos involved, whose name
and channel_type
are indicated as such. We indicate the role that the picos involved in the subscription will each play (my_role
and subscriber_role
).
When you visit this URL, the wrangler/subscription
event will be sent to the Mischief Pico and the subscription will be created. The ruleset in each child pico will approve the subscription request. This will result in each of the two picos involved having a subscription. Each of them will also have a new channel, specially created for use with the subscription. Here is the new channel created for the Thing1 pico:
Repeat the process for the Thing2 pico.
Listing Subscriptions
If we were now to query the Mischief Pico to see its subscriptions, using a URL like
http://localhost:8080/sky/cloud/ME9i5bo26xFPEUp9yBHUbq/io.picolabs.subscription/established
we would see that it participates in two subscriptions:
[ { "Rx_role":"controller", "Tx_role":"thing", "Rx":"Y3dpMrT12b5EzNKmdFsUKR", "Tx":"UHD1ku5PraSmRFq6zk9dwo" }, { "Rx_role":"controller", "Tx_role":"thing", "Rx":"7mkCJX1v3394umHtLuT2Gh", "Tx":"LGdgeySNYRdwtnP9y9yJ1n" } ]
Similarly, the Thing1 Pico participates in just one subscription. Doing the same sky/cloud
query to its ECI will show something like this:
[ { "Rx_role":"thing", "Tx_role":"controller", "Tx":"Y3dpMrT12b5EzNKmdFsUKR", "Rx":"UHD1ku5PraSmRFq6zk9dwo" } ]
Accepting Subscriptions
There is one step remaining in the manual subscription process. Just because one pico offers a subscription to another pico doesn't mean that target pico is forced to accept it. This is manifest by the "status" of the subscriptions. For the originating pico, the status is "outbound" (short for outbound, pending), and for the other pico, the status is "inbound" (i.e. inbound, pending).
In fact, after the pending subscriptions were created, the io.picolabs.subscription
rule for wrangler/subscription
raises the wrangler event "inbound_pending_subscription_added" passing along all of the attributes. It is up to your pico to react by either accepting or rejecting. This cookbook example shows how to write a rule that auto-approves subscriptions.
It is also possible to manually accept an inbound subscription request. Suppose, for the sake of example, that we later on create a new thing pico, named "Thing 3" and we want it to join the party. We install into it the rulesets io.picolabs.subscription and mischief.thing so now all that is left is getting it subscribed. If we started with the Mischief pico as above, the thing pico would automatically accept the subscription, so let's start instead with the thing pico. We would request a subscription by sending this event to the thing pico:
http://localhost:8080/sky/event/Cgzd9288VuKkvZDhHkMoeb/subscr/wrangler/subscription? wellKnown_Tx=ME9i5bo26xFPEUp9yBHUbq& Rx_role=thing& Tx_role=controller& name=thing3& channel_type=subscription
If we now want to see the inbound subscription requests of the mischief pico, we can use this URL:
http://localhost:8080/sky/cloud/ME9i5bo26xFPEUp9yBHUbq/io.picolabs.subscription/inbound
and see the pending subscription, which matches the event attributes associated with the event wrangler:inbound_pending_subscription_added
:
[ {"Rx_role":"controller", "Tx_role":"thing", "Tx":"WorT1aMaRxu39eW8Kk8ECy", "Rx":"4XB1c9YKvLnHygDa75VaYu" } ]
Generally, promiscuously approving pending subscriptions is a security risk, so most auto-approvals take place where something about the subscription requestor can be verified. For example, a child might be programmed to automatically approve subscription requests from its parent, after checking that the invitation came in on a channel that can be verified as being from the parent.
For now, to complete the manual process, we will send this event to the Thing 3 pico (line break added for readability–your URL will be all in one line):
http://localhost:8080/sky/event/4XB1c9YKvLnHygDa75VaYu/accept/wrangler/pending_subscription_approval?Rx=4XB1c9YKvLnHygDa75VaYu
Checking the Thing3 pico again, you will see that its one subscription is now established. At the same time, that subscription in the Mischief pico also now has the status of "established".
Deleting Subscriptions
You can cancel a subscription manually by sending an event to either of the picos involved in the subscription, in this example, the Thing3 pico.
http://localhost:8080/sky/event/4XB1c9YKvLnHygDa75VaYu/delete-subscription/wrangler/subscription_cancellation
Managing Subscriptions in the UI
Under construction
Support in the UI for subscriptions is under construction
The ability to manage subscriptions for a pico in the UI is under development.
Looking at established subscriptions
Click on the "Subscriptions" tab for a pico to see its established subscriptions.