Search code examples
droolsdrools-fusion

Drools event lifecycle


I've had to create pairs of rules to retract my events. It seems they don't expire. I had wanted one-and-done events. You can see below, they use the default duration, zero.

So for example, if I exclude the retraction rules and then insert the RemoveConnectionEvent first and then insert the CreateConnectionEvent, the RemoveConnection rule will still fire. (Using an agenda listener in my unit tests)

My expectation of an event was that RemoveConnectionEvent would be ignored, it would not do anything if its conditions were not met immediately. I did not expect it to hang around and trigger the RemoveConnection rule once that rules conditions were met when the NewConnection rule responded to the CreateConnectionEvent.

To get my rules to behave as I expected, I created RetractedCreation, RetractedRemoval, and RetractedUpdate. This seems to be a hack. I am imagining a declared my events wrong.

Any ideas?

ps This was a pretty good Q&A but I am not using windows. It might infer that perhaps my hack is an 'explicit expiration policy'.

Test Event expiration in Drools Fusion CEPTest Event Expiration

Here is my rule.

package com.xxx
import com.xxx.ConnectedDevice
import com.xxx.RemoveConnectionEvent
import com.xxx.CreateConnectionEvent
import com.xxx.UpdateConnectionEvent

declare CreateConnectionEvent @role( event ) end
declare UpdateConnectionEvent @role( event ) end
declare RemoveConnectionEvent @role( event ) end

rule NewConnection
    when
        $connection : CreateConnectionEvent($newChannel : streamId)
        not ConnectedDevice( streamId == $newChannel )
    then
        insert( new ConnectedDevice($newChannel) );
end

rule RetractedCreation
    when
        $creationEvent : CreateConnectionEvent($newChannel : streamId)
        exists ConnectedDevice(streamId == $newChannel)
    then
        retract($creationEvent)
end

rule RemoveConnection
    when
        $remove : RemoveConnectionEvent($newChannel : streamId)
        $connection : ConnectedDevice( streamId == $newChannel )
    then
        retract( $connection );
end

rule RetractedRemoval
    when
        $removalEvent : RemoveConnectionEvent($newChannel : streamId)
        not ConnectedDevice(streamId == $newChannel)
    then
        retract($removalEvent)
end

rule UpdateConnection
    when
        $connectionUpdate : UpdateConnectionEvent($newChannel : streamId) 
        $connection : ConnectedDevice( streamId == $newChannel )
    then
    $connection.setLastMessage();
end

rule RetractedUpdate
    when
        $removalEvent : UpdateConnectionEvent($newChannel : streamId)
        not ConnectedDevice(streamId == $newChannel)
    then
        retract($removalEvent)
end

Solution

  • This automatic expiry is a rather elusive feature. There's no concise definition when it'll work, and what needs to be done to make it work.

    In your apparently simple case where you don't use temporal operators and expect that events are to be retracted after they have matched one rule I'd adopt the following strategy without wasting another thought on "inferred expiration" and "managed lifecycle".

    Maybe you have a common (abstract) base class for your events; otherwise create a marker interface and attach it to all events. Let's call this type Event. Then, a single rule

    rule "retract event"
    salience -999999
    when
        $e: Event()
    then
        retract( $e );
    end
    

    will take care for all (Create, Update, Remove) events.

    Edit You may also use the explicit setting for event expiry.

    declare CreateConnectionEvent
        @role( event )
        @expires(0ms)
    end
    

    Make sure to use

    KieBaseConfiguration config = ks.newKieBaseConfiguration();
    config.setOption( EventProcessingOption.STREAM );
    KieBase kieBase = kieContainer.newKieBase( config );
    

    when creating the KieBase. I also recommend to "let the time pass", i.e., advance a pseudo clock or let the thread running a fireUntilHalt for a jiffy or two after fact insertion.