Writing Rules for Monitoring and Time-Sensitive Applications

After talking more abstractly about writing (hopefully better) rules, I would like to go down a bit and talk about real rules engines use cases and some ideas on how to write rules in such cases.

I sent an e-mail to the users list asking for cases that users would like to discuss in more detail and some suggestions started to arrive. In this post I will talk about a subject that was suggested by Neil Goldman that was also kind enough to share his thoughts about how to model rules for Monitoring and Time-Sensitive Applications.

It is not the scope of this post to talk about Event Stream Processing, that besides being related to the subject, have different requirements and are too extensive to be included in this single post.

I will use JBoss Rules (a.k.a. Drools) for the examples, but the ideas should possibly be used with any other engine.

Monitoring Applications

Monitoring Applications are applications that monitor and eventually take actions based on inputs from sensors (temperature, weather, movement, etc) or agents (network elements, managed systems, managed beans, etc). They usually have requirements of working as Near-Real-Time Applications and are designed to react to system state changes.

Question is: how do I architect my application leveraging the advantages of the rules engines to monitoring applications?

Lets discuss the case using a simple practical example: let’s say you have a monitoring application for your Air Conditioning System (ACS) that must control it to keep the temperature under a certain range.

One simple way of doing it using a Rules Engine is to work with a statefull working memory that reflects the state of the system and has rules that react to changes in this state. The state of the system, in the above example, is provided by the sensors, so it makes perfect sense to have your sensors modeled as facts. The rules will rely on these “sensor facts” to make decisions. So, your rules could be:


global org.drools.examples.acsmonitoring.AirConditioningSystem acs;

rule "turn ACS on"
when
TemperatureSensor( celsiusGrade > 25 )
then
acs.turnOn();
end

rule "turn ACS off"
when
TemperatureSensor( celciusGrade < 20 )
then
acs.turnOff();
end

The above are very simple rules, but they demonstrate the concept. A more elaborate rule could be able to work with different threasholds according to the weather input given by another sensor:


rule "turn ACS on"
when
WeatherSensor( $weather : weather )
TemperatureRange( weather == $weather, $max : maxTemperature )
TemperatureSensor( celciusGrade > $max )
then
acs.turnOn();
end

Having your constraints also modeled as facts (the TemperatureRange fact in the above example) also helps writing more robust rules, avoids the need for hardcoded values, and allows a single rule to react to several different conditions.

Well, we saw the “rules side” of your problem, but what your application needs to do? The answer is: simply keeps updating the sensors. That may be done in several ways, depending on how your specific system works, but in the end, it will usually be either by poll or by event. If you are polling your ACS, code would be like:


while( /* an exit condition */ ) {

sensorReader.update( sensor );
wm.modify(sensorHandle, sensor );
wm.fireAllRules();
// wait for the next polling slot

}

Monitoring applications may also work with stateless working memories, but a statefull working memory usually makes more sense for cases like described above.

Time-Sensitive Applications

Time-Sensitive Applications are applications that reason over or react to time. For example: billing applications, applications that run simulations, task schedulers, etc.

Rules in such applications will be directly or indirectly dependent on time. Here, it is important to not confuse the use of time as “data” to your rule and the use of time as “metadata” to your rule. Rules engines usually offer features to handle time as a metadata, like allowing one to specify a rule effective and expiration time, among other things.

What we are talking here is about reasoning over Time. And if we are reasoning over Time, it is clear that Time must be modeled as a fact. Quoting Neil Goldman:

” You do not want your rule conditions reading the operating system clock
as part of their match criteria, because you have very little control
over just when those criteria are evaluated. That clock is changing at a
fairly high frequency, and the rule engine is unaware of the sensitivity
of such criteria to these changes.
You want your rules to be sensitive to a pseudo-clock that
a) you can update in a controlled manner, and
b) the rule engine can be told about. “

Yes, you want a controlled clock to reason over. A good analogy is to think about the clock as a sensor from the previous examples. In the previous examples, the application controls when and how the sensor is updated as a way to keep the system state consistent. The same way the application can control when and how the rules pseudo-clock gets updated.

There are several advantages of this approach. We can list, among others:

  • system consistency: you can guarantee that no external, unpredicted or predicted event will cause inconsistencies in your facts (all sysadmins know the headaches of going back one hour in time when daylight savings period is over) and having controls inside your application to handle such situations is great.
  • test automation: if your rules are reasoning over a pseudo-clock, you can easily write test that advance the clock as needed to test specific scenarios, while in production, your pseudo clock may be synched with the real clock in predefined intervals.
  • ability to run simulations: so you have your Asset Management Application (considered the best of the breed) that has over 10000 rules to manage every single asset of your customer’s portfolio and your multimillionaire customer comes to you and ask “What if…?”. Any person used to work with simulation software knows that a basic requirement for one is a pseudo-clock. So, if your 10000 rules are written against a pseudo clock, adding simulation features to your application using the exact same 10000 rules is just a matter of manipulating the clock… on the other side, if your rules use a real clock, you either update your 10000 rules to support simulation or you manipulate the clock of your server… 🙂

It is important to note that, as mentioned above, there is no restriction to periodically synchronize your pseudo-clock with the system clock, as long as you have the control over when and how this update is done.

So, as promised, just to keep the post a bit more low level, a code example. Imagine you have a task scheduler application and you want to write rules that will run your tasks when the scheduled time arrives or when a previous required task is complete. Your rules could be like:


rule "Fire based on time"
when
PseudoClock( $currentTime : currentTime )
$t : Task( preReq == Task.PR_TIME, executedAlready == false, scheduledTime <= currentTime )
then
$t.execute();
end

rule "Fire based on task"
when
$pr : Task( firedAlready == true )
$t : Task( preReq == Task.PR_TASK, executedAlready == false, preReqTask == $pr )
then
$t.execute();
end

You probably noticed that the comparison between the scheduled time and the current time in the first rule above is made with a “<=” operator. This is needed because usually, there is no guarantee that your rule will be executed precisely at the given time (except when running in a real time platform), but “as soon as possible” after that time. So, it is a good practice to always constrain time using ranges or thresholds instead of absolute values.

Thank you to Neil for raising the topic. The above is just the tip of the iceberg, but I hope it is a start for deeper research for those interested.

Regards
Edson

comments

Author

Comments are closed.