Using Events to Speak to the Outside World

Introduction

Using events in Erlang is a great way to pass on information about what is going on in a running system. This might be sending alerts to some external system, sending an email in the middle of the night, or just logging some interesting information for later.

The event module in xqerl allows XQuery authors to send events asynchronously to the Erlang environment for processing there.

All that’s needed to process events from xqerl is an Erlang event handler that has been added to the locally registered event manager, xqerl_event_man.

The event module is rather simple as it only has one function (but that’s all we need).

Below is an example of using events in the simplest form, but should also give you an idea of what is possible in more complex cases.

Steps

  1. Write an Erlang module to handle events.
  2. Write an XQuery to produce events.
  3. Run it!

The Event Handler

Writing and using an event handler in Erlang is quite simple and is explained in more depth here.

Below is a simple handler that will handle events by printing them out to the console:

-module(xq_event_h).
-behaviour(gen_event).

-export([init/1, handle_event/2, terminate/2]).

init(_Args) ->
   {ok, []}.

handle_event({xqerl_event, <<"square">>, A, B}, State) ->
   io:format("***Got Square*** ~p^2 = ~p~n", [A, B]),
   {ok, State};
handle_event({xqerl_event, <<"even">>, A}, State) ->
   io:format("***Got Even*** ~p~n", [A]),
   {ok, State};
handle_event(_Event, State) ->
   {ok, State}.

terminate(_Args, _State) ->
   ok.

Producing events in XQuery

Sending out events is done with the statically bound functions event:notify#1, event:notify#2, event:notify#3 or event:notify#4. These functions will send a tuple in the form {xqerl_event, Arg1, ...}.

So, let’s make some events! Below is a simple XQuery to return numbers that are square (along with other busy work). It will send an event for each square, and another event for each of those that is an even number:

declare function local:is-square($a, $b)
{
  if (($a * $a) eq $b) then
    (
      event:notify('square', $a, $b),
      true()
    )
  else
    false()
};

declare function local:is-even($a)
{
  if (($a mod 2) eq 0) then
    (
      event:notify('even', $a),
      $a
    )
  else
    $a
};

for $i in 1 to 100
  , $j in 1 to 10000
where
  local:is-square($i, $j)
return
  $j => local:is-even()

Taking it for a spin

Assuming:

  1. Compile and load the Erlang module, add the handler
    • c("/tmp/data/xqerl/xq_event_h.erl").
    • gen_event:add_handler(xqerl_event_man, xq_event_h, []).
  2. Compile the main XQuery
    • Squares = xqerl:compile("/tmp/data/xqerl/squares.xq").
  3. Run the XQuery
    • xqerl:run(Squares).

You should see a bunch of lines reported to the console (from the event handler) and the return value of the query:

***Got Square*** 1^2 = 1
***Got Square*** 2^2 = 4
***Got Even*** 4
***Got Square*** 3^2 = 9
***Got Square*** 4^2 = 16
***Got Even*** 16
...
***Got Even*** 9216
***Got Square*** 97^2 = 9409
***Got Square*** 98^2 = 9604
***Got Even*** 9604
***Got Square*** 99^2 = 9801
***Got Square*** 100^2 = 10000
***Got Even*** 10000
[1,4,9,16,25,36,49,64,81,100,121,144,169,196,225,256,289,
 324,361,400,441,484,529,576,625,676,729,784,841|...]

Bam!! Producing events in XQuery and handling them in Erlang. Enjoy!