Events

Introduction

An event within an Inq environment originates from a node and propagates through that node's containment hierarchy. Entity typedef instances are examples of nodes that can be contained in multiple hierarchies and events raised on them propagate to all their containers. The section covering transactions discusses how such nodes raise events when they are mutated or deleted.

Not all container types solicit events from their children. When discussing map types we saw how only the hmap does this. Only node spaces built with containers of this type are event-live. The hmap itself can originate events and it does so when placed into or removed from a parent hmap using the add or remove functions.

Inq processes each have a node space private to them. When a process is started this node space comprises the single hmap yielded by the path $root. The association of a server-side user process with its corresponding client-side client process is covered in the discussion of Inq processes. If an event originating from a descendent reaches $root then, in a user process, Inq propagates this event to the client. We discuss below how this built-in handling often means that no explicit event listeners are required.

The other node space relevant to the topic of events is the global structure rooted at $catalog. Server-side processes must explicitly listen to this node if they want to be alerted of newly created typedef instances and other events occurring in their Inq environment.

Event Types and Generation

A primary design aim of Inq is that application script should have the least concern possible with infrastructural issues, allowing development to focus to the greatest extent on its function. To this end, there is no statement to raise an event. Instead, events are generated as a result of executing various Inq functions. While Inq also processes certain events, applications can arrange to dispatch them to script functions. The following event types are available, together with a summary of how they are raised:

update
Raised on typedef instances when mutations being managed in a transaction are successfully committed.
create
Raised on application typedefs when instance creations being managed in a transaction are successfully committed.
delete
Raised on typedef instances when deletions being managed in a transaction are successfully committed.
add
Raised on hmaps and typedef instances when added to an event-live container with the add function.
replace
As for add, above, when there is already a node at the given path within the node space.
remove
Raised on hmaps and typedef instances when removed from an event-live container with the remove function.
expire
Raised on the current application typedef when a new version replaces it after being successfully parsed.
cataloged
Raised on an application typedef when successfully parsed and entered into the catalog.
start
May be raised on a function or service when it starts executing.
complete
May be raised on a function or service when the transaction in which it was executed successfully commits.

Typedef Events

The update, create and delete event types relate to stages of a typedef instance's life-cycle and are refered to collectively as typedef events. Such events are raised by a transaction's commit phase, so they are only emitted when the transaction completes successfully. Update and delete events are processed by Inq as part of its coverage of common infrastructure tasks, discussed further later in this section. Create events have no internal handling, however all typedef events are always raised and cannot be switched off in any way.

The add and replace Events

These events are raised by the add function, which is used to place a node structure at some node space path:

"add" "("
         <node>
         ","
         <path>
         [ "," <raiseEvent> [ "," <proto> ] ]
      ")"

<node>       = <expression>
<path>       = <expression>
<raiseEvent> = <expression>
<proto>      = <expression>

The <node> argument, which must resolve, is placed at <path>. Usually <node> is the root of a structure created as described in Building Node Structures, however there is no restriction on what type node must resolve to.

The <path> argument is a node path yielded by the path function. Like a declaration, any containers within the path that are not already present will be created using the map type of the deepest container found unless the <proto> argument is used - see below.

In fact, add is very similar to a declaration and only differs in that it may raise an event on <node> after it has been placed at <path>. The <raiseEvent> argument defaults to true and if specified must resolve to a boolean. In any case, it is only relevant when <node> is an event generator, such as an hmap or a typedef instance. Under these circumstances, Inq raises an add or replace event, depending on whether there was already a node at the given path. For nodes that can exist in more than one containment hierarchy, the event only propagates through the hierarchy the node is being added to.

The <proto> argument, if used, must resolve to a map and will be used as the seed type for any containers that must be created in the path. Specifying the <proto> argument is unusual as continued use of the same map type when extending an existing structure is generally what is desired.

The return value of add is the <node> added. Here are some examples that have been taken from the petstore blueprint

  • if (any Account = call getAccount())
      add(Account, path($this.vars.Account));
    
  • service filterItems(any filter, any at)
    {
      add(call filterItems(filter), at);
    }
    
  • local function newOrderCreated(any Order, any listenInfo)
    {
      if (call isNewInstanceInDateRange(listenInfo.filter, Order))
      {
        // Make a node set child for the new Order
        hmap m;
        any m.Order = Order;
    
        // Add it into the list, causing it to appear on the GUI screen.
        any pKey = getprimarykey(Order);
        add(m, path($this.{listenInfo.at}.{pKey}));
      }
    }
    

The remove Event

The remove function removes a node from the penultimate path element used to reach it:

"remove" "("
            <node>
            [ "," <raiseEvent> ]
         ")"

<node>       = <expression>
<raiseEvent> = <expression>

The <raiseEvent> argument defaults to true. Unless overridden and if <node> is an event generator a remove event is raised and propagates through the containment hierarchy the node is being removed from.

The return value of remove is the <node> removed.

Single Parent Nodes

The hmap container type has the characteristic that it can only be resident in one parent hmap at a time (though it can reside in as many arrays, sets and smaps as desired because these containers do not listen to events from their children). Typically, an application builds event-live structures on the stack by seeding the map type. The structure is then placed into the permanent node space (somewhere beneath $this) to build long-lived application state and so that subsequent events are processed by Inq. The remove and add functions must then be used together to ensure that the single-parent condition of hmap is not violated. The following script creates an event-live node set of Cpty instances, assuming a suitable key k:

// Create the seed map of the appropriate type
hmap m;

// Read the list and target it at $stack.m - it is then made from hmaps
read(Cpty,
     k,
     target=m,
     setname="someList");

// Move the list into the context
add(remove(m.someList),
    path($this.vars.cptyList));

The seed map is then discarded when the stack frame is unwound. The diagram below depicts what is happening:

building event live structures

The expire Event

An expire event is raised on a typedef when either

  1. an existing typedef is reparsed from its source file or
  2. the expire function is called for the given typedef.

Expiring a typedef causes all its currently loaded instances to become unmanaged, meaning that they will no longer participate in transactions, be written to any external storage or emit typedef events.

Reparsing a Typedef

Inq applications are intended to continue running in the face of reloading parts of their server-side environment, however such occurrances mean that existing node structures containing instances of those typedefs must be reconstructed if they are to remain viable event-live ones.

Expiring a Typedef

A typedef can be expired using the expire function:

"expire" "("
          <type-reference>
         ")"

Ideally, all manipulation of typedef instances occurs within the Inq server, including such tasks like bulk loading of externally sourced data. In that case Inq maintains the integrity of the loaded data set and informs all processes of application execution through transaction management, instance locking and typedef events. Often, however, Inq will be deployed in an environment where there are legacy concerns, such as data feeds directly into database tables or daily life-cycle jobs implemented in SQL servers.

Jobs running outside an Inq environment that undermine it at the typedef level should arrange to signal their completion with the appropriate expire statements using the inq -load command, discussed in Invoking Inq. Applications that operate in this kind of environment must be written to handle expire events, described in setting up event listeners, below.

The catalog Event

Listening for Events

Note
To Be Continued