Skip to content

Instantly share code, notes, and snippets.

@nlacasse
Created April 12, 2012 19:10
Show Gist options
  • Save nlacasse/2370220 to your computer and use it in GitHub Desktop.
Save nlacasse/2370220 to your computer and use it in GitHub Desktop.
subscriptions blog post

Keeping track of channel subscriptions

One feature request that developers often ask for is a way to tell which clients are listening for messages on a channel.

For example, a chat application needs to know what users are in each chat room at any given point in time.

Or, to take a more complex example, say you have a distributed logging setup where log data is generated by various clients and sent to multiple servers (some store the data long-term, others parse the data in real time and send alerts if necessary, etc).

Since log data is important, we would like to know which servers are currently listening for data. Furthermore, we should be notified when servers come online or go offline.

The Spire API makes this very easy.

Each Spire channel knows which subscriptions are currently listening on that channel, and exposes the names of these channels through a channel_subscriptions resource described below. In addition, channels send special events called "joins" and "parts" whenever a new subscription is created or deleted.

Using these two mechanisms, it is a simple matter to keep track of all subscriptions listening to a particular channel.

Find all subscriptions to a channel

All channels now have a channel_subscriptions resource inside them. Making a GET request to that resource will return all the named subscriptions to the channel.

Using the javascript client, we can find all the subscriptions for the channel chan with code like the following:

// `chan` is a channel object defined elsewhere

chan.subscriptions(function (err, subs) {
  // `subs` will be a hash of subscription_name => subscription
});

This code assumes that you already have a channel object called chan.

If we don't have the channel object, but know the channel name, we can get the subscriptions for the channel with a function like this:

function getSubsForChannel (channelName, cb) {
  // First, get the channel.
  spire.channel(channelName, function (err, chan) {
    if (err) return cb(err);

    // Then get all the subscriptions.
    chan.subscriptions(function (err, subs) {
      if (err) return cb(err);
      cb(null, subs);
    });
  });
}

This function takes a channel name and callback as arguments. It fetches the channel, and then fetches the subscriptions for the channel. If there are no errors, the callback will be called with the subscription hash.

Getting back to our distributed logging example, finding all the servers listening for log data on channel "logs" is as simple as this:

getSubsForChannel("logs", function (err, subs) {
  if (!err) {
    // `subs` has all servers subscribing to log data
  }
});

Once we have the list of current subscriptions, we want to keep it up to date. We could just poll the channel subscriptions resource, but that's not very elegant. Instead, we can use subscriptions to listen for subscription creation and deletion events, just like we use subscriptions to listen for message events.

Subscription events

In addition to the usual "message" events (which correspond to messages published to a channel), there are two new kinds of events: "joins" and "parts".

A "join" event is created whenever a subscription is created to a channel. A "part" event is created whenever a subscription is deleted.

Once you have a subscription to a channel, GETting that subscription will return a hash that looks like this:

{
  joins: [],
  parts: [],
  messages: [],
  first: 12345,
  last: 12346
}

The messages array contains messages that have been published to the channel.

The joins array contains an event for each subscription that was created. It will contain the subscription name and timestamp.

Similarly, the parts array contains an event for each subscription that was deleted.

The first and last are timestamps corresponding to the first and last events returned. They are there for convenience, so you don't need to inspect the arrays to find the first and last timestamp yourself.

With the javascript client, you add listeners for joins and parts the same way you add listeners for messages:

  // `sub` is a subscription created elsewhere

  // Add a listener for published messages
  sub.addListener('messages', function (messages) {
    // `messages` is an array of message events
  });

  // Add a listener for new subscriptions
  sub.addListener('joins', function (joins) {
    // `joins` is an array of join events
  });

  // Add a listener for deleted subscriptions
  sub.addListener('parts', function (parts) {
    // `parts` is an array of part events
  });

  sub.startListening({ });

For our logging example, we want to be notified when a log server goes on or offline, so our listeners should send some type of notification.

Caveat

Currently, the only way to remove a subscription from a channel is to explicitly DELETE it.

sub.delete(function (err) {
  if (!err) {
    // sub successfully deleted
  }
});

This deletion removes the subscription from the list of current channel subscriptions, and triggers a part event.

We are working on a new feature that will allow subscriptions to timeout after a set period of time. After this feature is released, if a user disconnects from your application or navigates away from the page, Spire will delete the subscription for you and send the "part" event.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment