Copy of Pico to Pico Subscriptions Lesson

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:

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. 

In this diagram, from the perspective of pico A, the top channel is the one on which it receives incoming events and queries, so A knows this channel as Rx, and the bottom channel is the one on which pico A transmits events and queries to pico B, so A knows this channel as Tx. Conversely, pico B knows the top channel as Tx and the bottom channel as Rx. Each pico has its own perspective on the subscription.

Subscriptions may also include a role for each pico. From the perspective of pico A, its role in the subscription is known as Rx_role while pico A knows the role of pico B as Tx_role. On the other hand, from pico B's perspective, the role of pico A is Tx_role and its own role is Rx_role. Putting yourself in a pico's place, you can think of Rx_role as my role and Tx_role as the other's role.

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.

  1. Install this ruleset into your root pico: https://raw.githubusercontent.com/Picolab/pico_lessons/master/subscriptions/mischief.owner.krl
  2. Install this ruleset into your Mischief pico: https://raw.githubusercontent.com/Picolab/pico_lessons/master/subscriptions/mischief.krl, and 
  3. 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

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").head();
  }
  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.

Copyright Picolabs | Licensed under Creative Commons.