Wednesday, December 23, 2015

GenRouter

In my last post, I briefly mentioned a new Elixir module named GenRouter. José Valim described its rationale in his Elixir Conf 2015 (US) keynote address. The project is in development and can be seen on GitHub. As with all Elixir core code, it is superbly documented - even before it has been fully implemented - although they do not seem to have settled on a naming convention. For example, the terms source and sink are used for what FBP calls in and out ports.

The name GenRouter was chosen to reflect the idea that it is a generic module  similar to the the other Elixir generic libraries: GenServer and GenEvent. Here are the opening paragraphs from the current documentation:
A behaviour module for routing events from multiple sources to multiple sinks. 
GenRouter allows developers to receive events from multiple sources and/or send events to multiple sinks. The relationship between sources (the one sending messages) and sinks (the one receiving them) is established when the sink explicitly asks for data. 
Since the same process can send and receive data, the same process can be both a source and sink, which is quite common. Sources that only send data and sinks that only receive data are called “definite source” and “definite sink”, respectively.
In his talk José acknowledged talking with the Akka team. Akka is a member of the Reactive Stream Initiative, which among other things has released a specification on how well-behaved processes handling streams of data are to act, especially with regard to "back pressure". I've mentioned Reactive Streams in early posts; its specification has also influenced my design. Basically, there is two-way communication between entities that are sending and/or receiving data steams. Demand requests are sent upstream and the fulfillment of these demand requests are sent downstream. Hence, nothing moves unless there is a demand for something.

The Reactive Streams influence on GenRouter is manifested in a module named GenRouter.Spec. In the module there are four "convenience" functions that one can use to:
  1. subscribe() to an upstream source, asking for some number of data events to be sent. So the Subscriber is asking a Publisher for a Subscription and for a certain maximum number of events (data). The Subscriber must honor the parameters of this Subscription
  2. ask() for data from an upstream source, specifying how many data events an entity can receive.
  3. route() is used to send data to a downstream entity. The Subscriber sends data to a subscribed Publisher.
  4. unsubscribe() will cancel the source/sink relationship. The Subscriber will attempt to cancel the Subscription that it has with a Publisher.
As of this date GenRouter needs to be "fleshed out".