Temporal contexts
Imagine that we have seven event templates defined, which for the sake of brevity are represented by the letters A, B, C, D, E, F and G in the following text. Now, consider a stream of incoming events, where Xn indicates an event instance that matches the event template X. Likewise, Xn+1 indicates another event instance that matches against X, but which need not necessarily be identical to Xn.
Consider the following sequence of incoming events:
C1 A1 F1 A2 C2 B1 D1 E1 B2 A3 G1 B3
Given the above event sequence, what should the event expression
A -> B
match upon?
In theory the combinations of events that correspond to an A followed by a B are:
{A1, B1}, {A1, B2}, {A1, B3}, {A2, B1}, {A2, B2}, {A2, B3}, {A3, B3}
In practice it is unlikely that a developer wanted their monitor to match seven times on the above example sequence, and it is uncommon for all the combinations to be useful.
In fact, consistent with the truth-value based matching behavior already described, the event expression A -> B will only match on the first event sequence that matches the expression. Given the above event sequence the listener will trigger only on {A1, B1}, call the associated match method, and then terminate.
If a developer wishes to alter this behavior, and have the monitor match on more of the combinations, they can use the all operator within the event expression.
If the listener's specification was rewritten to read:
all A -> B
the listener would match on every A and the first B that follows it.
The way this works is that upon encountering an A, a second child listener (or sub-listener) is created to seek for the next A. Both listeners would continue looking for a B to successfully match the sequence specified. If more As are encountered the procedure is repeated; this behavior continues until the parent listener is explicitly deactivated.
Therefore all A -> B would match on {A1, B1}, {A2, B1} and {A3, B3}.
Note that all is a unary operator and has higher precedence than ->, or and and. Therefore all A -> B is the same as (all A) -> B or ( ( all A ) -> B ).
The following table illustrates how the execution of on all A -> B proceeds over time as the above sequence of input events is processed by the correlator. The timeline is from left to right, and each stage is labeled with a time tn, where tn+1 occurs after tn. To the left are listed the listeners, and next to each one (after the ?) is shown what event template that listener is looking for at that point in time. In the example, assuming L was the initial listener, L', L'' and L''' are other sub-listeners that are created as a result of the all operator.
Guide to the symbols used:
This symbol | indicates the following |
| A specific point in time when a particular event is received. |
| No match was found at that time. |
| The listener has successfully located an event that matches its current active template. |
| A listener has successfully triggered. |
| A new listener is going to be created. |
The parent listener denoted by all A -> B will never terminate as there will always be a sub-listener active looking for an A.
If, on the other hand, the specification is written as,
A -> all B
the listener would now match on all the sequences consisting of the first A and each possible following B.
The way this works is by creating a second listener upon matching a B that then goes on to search for an additional B, and so on repeatedly until the listener is explicitly killed.
Therefore A -> all B would match {A1, B1}, {A1, B2} and {A1, B3}.
Graphically this would now look as follows:
The table shows the early states of L' and L'' in light color because those listeners actually never really went through those states themselves. However, since they were created as a clone of another listener, it is as though they were.
The parent listener denoted by A -> all B will never terminate, as there will always be a sub-listener looking for a B.
The final permutation is to write the monitor as,
all A -> all B
Now the listener would match on an A and create another listener to look for further As. Each of these listeners will go on to search for a B after it encounters an A. However, in this instance all listeners are duplicated once more after matching against a B.
The effect of this would be that all A -> all B would match {A1, B1}, {A1, B2}, {A1, B3}, {A2, B1}, {A2, B2}, {A2, B3} and {A3, B3}, i.e. all the possible permutations. This could cause a very large number of sub-listeners to be created.
Note:
The all operator must be used with caution as it can create a very large number of sub-listeners, all looking for concurrent patterns. This is particularly applicable if multiple all operators are nested within each other. This can have an adverse impact on performance.
As with all other event expression operators, the all operator can be used within nested event expressions, and be nested within the operating context of another all operator. This can have a dramatic effect on the number of sub-listeners created.
Consider the example,
all (A -> all B)
This will match the first A followed by all subsequent Bs. However, as on every match of an A followed by B, (A -> all B) becomes true, then a new search for the next A followed by all subsequent Bs will start. This will repeat itself recursively, and eventually there could be several concurrent sub-listeners that might match on the same sequences, thus causing duplicate triggering.
On the same event sequence as previously, graphically, this would be evaluated as follows:
Thus matching against {A1, B1}, {A1, B2}, {A1, B3}, and twice against {A3, B3}. Notice how the number of active listeners is progressively increasing, until after t12 there would actually be six active listeners, three looking for a B and three looking for an A.