• No results found

Production rules are not an event query language as such, however they offer a fairly convenient and very flexible way of implementing event queries. The primary reason that production rules are more convenient for implementing event queries than a general purpose programming language is that their forward-chaining evaluation algorithm is very similar to the algorithms used for evaluating event queries (see Chapter 12). In addition, many business rules management systems (BRMSs) that are based on production rules offer support for defining domain-specific languages (DSLs). A domain-specific languages hides the syntax of production rule languages as well as some intricacies of the object model behind an interface of controlled natural language.

The first successful production rule engine has been OPS [FM77], in particular in the incar- nation OPS5 [For81]. Since then, many others have been developed in research and industry, including Drools (also called JBoss Rules) [JBo], ILOG JRules [ILO], and Jess [San]. The exam- ples in this section are in the Drools Rule Language (DRL), but other production rule languages 10Note that many data stream management systems were perceived for processing high throughput (e.g., stock tick data from financial markets), where there might be the implicit assumption that there is always at least one tuple per time instant being received. In these cases there would be no difference. However, when data stream languages are applied in other contexts, this assumption might not be sensible.

are very similar. While the general ideas of production rules will be explained here, we refer to [BBB+07] for a deeper introduction.

A production rule, sometimes also called condition-action (CA) rule (in contrast to event condition action rules), is a statement of the form WHEN condition THEN action. It specifies that the action is to be executed whenever the condition becomes true. It should be emphasized that the action is only executed once when the conditionbecomes true (i.e., whenever its truth value changes from false to true), not every time the condition holds (i.e., has the value true).

The condition is evaluated over a set of facts called the working memory. A production rule engine is usually tightly coupled to a general purpose programming language such as Lisp or Java. Accordingly, facts are typically represented in the data model of that host programming language, i.e., as Lisp terms or Java objects. Facts must be explicitly inserted into or deleted from the working memory. The operations a production rule engine offers for this are usually called assert11 andretract.

There are no restrictions on the action, it can be an arbitrary method or procedure call in the host programming language. Particularly interesting are actions that change the working memory by asserting new facts, retracting existing facts, or updating existing facts. Production rules can be used to implement deductive inferences by simply asserting the deduced facts. To this end, many production rule languages also offer an assert logical action (in addition to the standard assert), which has the effect that the asserted fact is automatically retracted when its associated condition becomes false again.

Production rules are evaluated in a forward-chaining manner in so-called match-act cycles. Their evaluation must be explicitly invoked with a method call such asfireAll()to the produc- tion rule engine from the host programming.12 When invoked, the production rule engine checks all rule conditions against the working memory (“match”). From all rule instances that can fire, it selects a single one according to some conflict resolution strategy. The action of this single rule instance is then executed (“act”) and possibly modifies the working memory. This match-act cycle is then repeated until there are no rule instances that can fire. (Note that non-terminating rule sets are possible.) Algorithms such as rete [For82] can realize the matching in a fairly efficient manner by incrementally updating the results of the match phase when the working memory changes and thus avoiding to re-check every condition in every match-act cycle.

3.4.1

General Idea

Production rules can be used to implement event queries “manually” by (1) asserting some fact for each event that happens and (2) writing each event query as a condition over these facts. To ensure timely detection of complex events, the evaluation of the production rules must be invoked (withfireAll()) after each assertion of an event fact. Using production rules for CEP essentially means that a complex event query is transformed into an expression over the state of the working memory.

Figure 3.5 shows how the complex event that an order has been completed (again, this consist- ing of order, shipping, and delivery) might be realized with a production rule in the Java-based Drools Rule Language (DRL). The rule assumes that Java objects of classesOrder,Delivery, and Shipping are asserted in the working memory whenever respective events happen. The classes have attributes for the order number (Order.idandShipping.orderIdand the tracking number (Shipping.trackingIdandDelivery.id). The rule fires and performs its action (simple console output), whenever (1) a combination of objectso,s,dof the three respective classes can be found in the working memory that satisfies the conditions on the order number and tracking number and (2) the rule has not already been fired before for this particular combination.

11Since many newer programming languages use “assert” as a keyword, the assert operation is now also often renamed to “insert” to avoid clashes.

12More precisely, the method call is usually not directly to the production rule engine object, but rather a so-called session object which bundles an instance of a working memory together with one or more rule sets.

rule " D e t e c t c o m p l e t e d o r d e r s " when o : O r d e r () , s : S h i p p i n g () , d : D e l i v e r y () , o . id == s . orderId , s . t r a c k i n g I d == d . id then S y s t e m . out . p r i n t l n (" O r d e r " + o . id + " c o m p l e t e d . " ) ; end

Figure 3.5: A production rule for detecting “order completed” complex events

rule " D e t e c t c o m p l e t e d o r d e r s " when o : O r d e r () , s : S h i p p i n g () , d : D e l i v e r y () , o . id == s . orderId , s . t r a c k i n g I d == d . id , H e l p e r . w i t h i n H o u r s ( o . t i m e s t a m p , s . t i m e s t a m p , 24) , H e l p e r . w i t h i n H o u r s ( s . t i m e s t a m p , d . t i m e s t a m p , 48) , then S y s t e m . out . p r i n t l n (" O r d e r " + o . id + " c o m p l e t e d on t i m e . " ) ; end

(a) Production Rule

import j a v a . u t i l . C a l e n d a r ;

public static class H e l p e r {

public static b o o l e a n w i t h i n H o u r s ( C a l e n d a r t1 , C a l e n d a r t2 , int d ) { C a l e n d a r t 1 P l u s D = t1 . c l o n e (). add ( C a l e n d a r . HOUR , 2 4 ) ;

return t2 . b e f o r e ( t 1 P l u s D ); }

}

(b) Auxiliary Java class

Figure 3.6: Production rules and temporal conditions

3.4.2

Temporal Aspects

Production rules are designed to operate on facts, not events. Therefore, production rule languages typically offer no designated construct for expressing the temporal aspects commonly needed in event queries. However, it is possible to some degree to implement such temporal aspects of an event query as a condition on attributes of event object.

For example, each event object could carry an attributetimestampthat signifies the occurrence time of the event. Whether this is a time point or a time interval is a design choice left to the programmer. Because production rules rely on the types and functions that are available in their host programming language and because time points are better supported in most programming languages, using time points might often be convenient where they are sufficient.

Consider a refinement of the “order completed” complex event to a “order completed on time” complex event. This event has as additional constraint that an order must be shipped within 24 hours, and the shipping delivered within 48 hours. When the respective classes for the order, ship- ping, and delivery event have an additional attributetimestamp(e.g., of the Java typeCalendar, which represents time points), then these temporal constraints can be expressed as conditions on these attributes. Figure 3.6(a) shows a modified version of the earlier “order completed” rule from Figure 3.5, where the time constraints for “order completed on time” have been added. This rules makes use of an external helper functionwithinHoursthat realizes the necessary temporal com- putations on the JavaCalendarobjects. A possible Java implementation of this helper function

is shown in Figure 3.6(b).

While it is possible to express temporal conditions as conditions on attributes of events, pro- duction rules offer no guidance to programmers on how to express such temporal conditions and how to design their event classes appropriately (e.g., with attributes for time stamps). As will be- come evident later, the way the example rules given here are written is influenced significantly by the design of XChangeEQ. Neither production rule languages nor Java offer sufficient support for typical temporal conditions of event processing. Helper functions such as the one in Figure 3.6(b) will therefore be needed quite often. Note that such helper functions can have arbitrary side- effects. In the example it would be easy to forget calling clone()and thus mistakenlymodify a time stamp of an object in the working memory in the helper method. Finally, the evaluation of production rules is unaware of the special semantics of temporal conditions and cannot use them to optimize query evaluation.

Production rules also offer little support for temporal events. If temporal events, be they relative (e.g., “12 hours after event X”) or absolute (e.g., “at noon”), are needed in an event query, then a fact must be asserted at the time this temporal event is supposed to happen. This must be done externally from the production rule engine in regular Java code. This in turn entails the use of fairly low-level functionality to schedule the execution of a thread that asserts the corresponding fact for execution an a given time (e.g., using Java’sTimerclass) and a potential for race conditions. Also, it violates the principle that the event processing logic should be encapsulated in the production rules: it can then not be simply changed by changing the production rules, other (external) code must be changed as well.

Some production rule languages allow to delay the execution of a rule by a given duration (in Drools this is expressed thedurationkeyword). This might be used in some cases as a substitute for relative temporal events, but is fairly limited.

3.4.3

Garbage Collection

Asserting a fact for each event that happens raises a practical concern: over time, the number of facts and thus the size of the working memory grows and grows. Since we often assume unbounded streams of events in event processing, we will eventually run out of memory if we only assert facts and never retract them. To avoid this we need some way to retract event facts that have become irrelevant, i.e., some kind of garbage collection of event facts.

Since current production languages are not designed to work with events and their temporal relations, they usually offer no help for garbage collecting event facts. Note also that the automatic garbage collection of the host programming language (e.g., Java) will not help: the working memory keeps a list of references to the objects representing its facts, so that any object in the working memory is accessible and will never be subjected to automatic garbage collection. Essentially, the programmer is left with programming garbage collection of event facts manually. This can be done either by writing further production rules that retract events that have become irrelevant or externally in the host programming language.

Garbage collection raises the question what it means for an event to be relevant, an issue that will also be the subject of Chapter 15 of this thesis. A simple method might be to define a default timeout for each event type. Any event older than the timeout is then deemed irrelevant. However, this definition influences the logic and semantics of event queries since different timeouts might lead to different query results.

In general, it would be preferable to use temporal (and other) conditions in the queries to figure out when an event becomes irrelevant. Consider again the “order completed on time” query. We can reason that a shipping event that is older than 48 hours is irrelevant to this particular query, since there will be no joining delivery event that satisfies the constraint that shipping and delivery must happen within 48 hours. However, for the order event things are more complicated. It might seem that the same reasoning can be applied to deduce that it becomes irrelevant after 24 hours. However this is not correct: if a joining shipping event has been found within those 24 hours, the event must be kept in the working memory to join order and shipping with a later delivery event. Therefore an order event might be relevant for up to 72 hours. Further, this reasoning only

concerns the single “order completed on time” query. There might be further event queries, e.g., a query to aggregate orders over a full week, that will lead to different relevance times.

We can see from this that figuring out whether an event fact is irrelevant or not —and thus can be garbage collected or not— can be hard. Therefore programming garbage collection manually is hard. Since an incorrect garbage collection will crucially affect the correctness of event queries and might lead to memory leaks, manual garbage collection of events is also somewhat dangerous. Further it leads to code that is hard to change and maintain: adding a single production rule for a new event query might affect the relevance of the event facts it accesses and thus entails adapting the code for garbage collection.

There has been some research on extending production rule engines and languages for event processing [Ber02, WBG08]. In particular, production rules are extended in [WBG08] by adding event composition operators and an automatic garbage collection of events. This work will be discussed in Section 3.6.3. Such approaches have not yet found their way into current production rule engines; however one can expect to see more work on this in the near future.

3.4.4

Negation and Aggregation

Negation and aggregation are supported in many production rule languages. For negation, the notconstruct makes it possible to test for the absence of certain facts in the working memory. For aggregation, facts can be collected into lists (using a list type of the host programming language such as List in Java) with a construct such as collect or the more general accumulate. The action of a rule can then use this list to compute an aggregate such as the average over an attribute. Note that this aggregate must usually be programmed manually in an iteration over the list. (A simple count aggregate is an exception, because lists typically provide a function giving their length.) In particular, the iteration must also take care of grouping and duplicate elimination if these are part of the event query specification.

The constructs for negation and aggregation are of course intended for processing normal facts rather than events happening over time. Their typical use in event queries, e.g., aggregation over a sliding time window or negation (absence) of an event between two other events, are therefore hard to express. Again this requires the use of conditions on time stamp attributes and similar mechanisms.

State-based processing, which we look at next, can in some cases also be used for expressing event queries involving negation or aggregation in production rules. It thus provides an alterative for the constructsnotand accumulate. When to use state-based processing or these constructs, is a design choice left to the programmer and often not an easy one.

3.4.5

State-based Processing

A strength of production rules is that they allow to combine state-based information with event- based information. This is not surprising: Production rules are after all primarily intended for state-based processing, i.e., performing specific actions when the working memory enters specific states. When using production rules for event processing, asserting a fact for an event that happens can be understood as a state where that event has happened.

Some situations which might be perceived as a complex event at first glance are easier to specify and detect as a certain state rather than through a complex event query. Consider a room that is equipped with sensors signaling eventsenterandleavewhenever a person enters or leaves the room through its door. An application for climate control might require to detect situations where more than three persons are in the room. Expressing this situation as a complex event, i.e., a combination of events happening over time, is typically hard: it is not simply a sequence of three enter event, because (1) we must know how many persons are in the room when the complex event query is started and (2) usually there will beleaveevents that cancel the effect a precedingenter event.

Using production rules and state-based processing, however, this situation if fairly easy to detect. We simply maintain a single fact to record the number of persons in the room. This fact

just has a single counter attribute of type integer. Whenenterandleaveevents happen, we will not assert facts for these events in the working memory. Rather we will only increase or decrease the counter. A production rule for detecting the situation where more than three persons are in the room then is simple to write (here in pseudo code): WHENcouter >3 THENaction. Note that it is fairly ease to incorporate new events that affect the number of persons in the room (e.g., persons entering through the window instead of the door) into this scenario.

In contrast, both composition-operator-based languages and data stream languages will have significant difficulties for detecting such a situation. (One possible solution might be to count