...
Guard conditions in rules are usually the best way to ensure idempotence idempotency in rules because they can be used to only take action have an effect when specific conditions are met.
Unfortunately, there are some functions in KRL (notably in the PCI and (Classic) RSM modules) that make state changes (i.e. have persistent effect). When these are used, they can cause side effects before the rule's guard condition is executed. This is a design flaw in KRL that will be rectified in future versions of the language. These functions should have been actions rather than functions so that they can sit after the guard condition.
In the meantime, a guard A guard rule offers a useful method for assuring idempotency in rules. The basic idea is to create two rules: one that tests a guard condition and one that carries out the rule's real purpose.
...
The following is the guard rule for the Fuse initialization (assuming pds is a module providing persistent storage for multiple rule sets):
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
rule kickoff_new_fuse_instance { select when fuse need_fleet pre { fleet_channel = pds:get_item(common:namespace(),"fleet_channel"); } is_null = if(fleet_channel.isnull()) then} {if(is_null) then send_directive("requesting new Fuse setup"); } fired { raise explicit event "need_new_fleet" with attributes { "_api" =: "sky", and "fleet" =: event:attr("fleet") || "My Fleet"; } else { } log ">>>>>>>>>>> Fleetlog channelwarn exists:<<Not "creating +new fleet_channel; fleet channel log ">> not creating new fleet "; exists: #{fleet_channel}" if is_null } } |
The guard rule merely looks for a only continues if the fleet channel is null (evidence that a fleet doesn't already exists) and only continues if the fleet channel is null. exist).
Note |
---|
The rule below needs updating from the way the old pico engine did things. |
The second rule does the real work of creating a fleet pico and initializing it.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
rule create_fleet { select when explicit need_new_fleet pre { fleet_name = event:attr("fleet"); pico = common:factory({"schema": "Fleet", "role": "fleet"}, meta:eci()); fleet_channel = pico{"authChannel"}; fleet = {"cid": fleet_channel}; pico_id = "Owner-fleet-"+ random:uuid(); } if (pico{"authChannel"} neq "none") then every { send_directive("Fleet created") with cid = , {"cid":fleet_channel}); // tell the fleet pico to take care of the rest of the initialization. event:send(fleet, "fuse", "fleet_uninitialized") with attrs = {"fleet_name": fleet_name, "owner_channel": meta:eci(), "schema": "Fleet", "_async": 0 // we want this to be complete before we try to subscribe below }; } fired { // put this in our own namespace so we can find it to enforce idempotency raise pds event new_data_available with namespace = common:namespace() and keyvalue = "fleet_channel" and value = fleet_channel and _api = "sky"; // make it a "pico" in CloudOS eyes raise cloudos event picoAttrsSet with picoChannel = fleet_channel // really ought to be using subscriber channel, but don't have it... and picoName = fleet_name and picoPhoto = common:fleet_photo and picoId = pico_id and _api = "sky"; // subscribe to the new fleet raise cloudos event "subscribe" with namespace = common:namespace() and relationship = "Fleet-FleetOwner" and channelName = pico_id and targetChannel = fleet_channel and _api = "sky"; log ">>> FLEET CHANNEL <<<<"; log "Pico created for fleet: " + pico.encode(); raise fuse event new_fleet_initialized; } else { log "Pico NOT CREATED for fleet"; } } |
...