TQL Policy

Introduction

A Policy is  a combination of a Trigger and an Action.


Trigger is a mechanism to specify conditions when given action needs to be executed. You can think about triggers as [complex] Boolean expressions over various events and constraints. An example of an event would be the fact that some [boolean] attribute of some instance got modified and an example of a constraint would be the check that it was assigned a new value of “false”.  Constraints and conditions are similar to the one used in TQL Find queries. So Trigger language looks a lot like Find language. 

Example

Here Light.state is the [target] attribute in question and we define trigger event as pseudo-function $Updated() meaning that policy engine must watch for updates of Light.state attribute in each and every instance, but only react when assigned value is “on”. This is equivalent to subscribing to TQL.Update.<namespaces>.Light.State.* topic and making your action to check that new assigned value is “on”.

Sample Policy Trigger
<Trigger>
  <Light.state value="$Updated()" eq="on"/>
</Trigger>

Trigger Events -  are always related to the attribute value itself. Therefore events are:

  • $Created()
  • $Updated()  - This is the default event if none specified.
  • $Deleted()

Conditions - syntax is the same as in Find Query.

Note also that while Find queries are about model instances and [attribute values -based] correlations between them, triggers are about attributes and changes of their values. In other words, Find queries are about something located in space (i.e. stored values) while trigger events happen in time.

TQL Policy Definition

TQL Policy is internally implemented as a "Thing".

The full set of definitions is the following:

TQL Policy Definiton
<Def name="CodeFragment" inherits="Fragment,DataFacet">
  <String name="FragmentContent" cardinality="0..1" format="$ObjectFormat()"/>
</Def>

<Def name="TriggerDefinition" inherits="CodeFragment" documentation="Base type for all trigger definitions"/>
<Def name="ActionDefinition" inherits="CodeFragment" documentation="Base type for all action definitions"/>
<Def name="TqlPolicyTriggerFacet" inherits="TriggerDefinition"/>
<Def name="TqlPolicyActionFacet" inherits="ActionDefinition"/>
<Def name="TqlPolicyTriggerModel" inherits="TqlPolicyTriggerFacet,DataModel">
   <Sid name="TriggerId"/>
</Def>
<Def name="TqlPolicyActionModel" inherits="TqlPolicyActionFacet,DataModel">
  <Sid name="ActionId"/>
</Def>
<Def name="TqlPolicyFacet" inherits="ThingFacet">
   <Reference name="Trigger" type="TqlPolicyTrigger" cardinality="1"/>
   <Reference name="Action" type="TqlPolicyAction" cardinality="1"/>
   <Long name="TriggerTime" KnownBy="DefaultPolicyAction"/>
   <Action name="DefaultPolicyAction">
      ...
   </Action>
</Def>
<Def name="TqlPolicyModel" inherits="TqlPolicyFacet,ThingModel">
   <Sid name="PolicyId"/>
</Def>

<TqlPolicyTriggerModel name="TqlPolicyTrigger" unique="FragmentContent"/>
<TqlPolicyActionModel name="TqlPolicyAction" unique="FragmentContent"/>
<TqlPolicyModel name="TqlPolicy"/>

<DataModel name="System" modifiers="system,hidden,readonly">
   <Long name="CurrentTime"/>
</DataModel>
Simple Definition of TQL Policy
<ThingModel name="TqlPolicy">
  <Reference name="Trigger" type="TqlPolicyTrigger" cardinality="1"/>
  <Reference name="Action" type="TqlPolicyAction" cardinality="1"/>
  <Long name="TriggerTime" KnownBy="DefaultPolicyAction"/>
  <Action name="DefaultPolicyAction"/>
</ThingModel>


TQLPolicyFacet

A new facet type SffTqlPolicyFacet is introduced that extends from SffTqlFacet. Everything you can do with TQL facet can be done with Policy Facet as well.

Note that TqlPolicyFacet requires SffBdlFacet reference as policies are implemented / enforced using Behavior trees internally within A-Stack.

NewFacetInstance using SffTqlPolicyFacet
<NewFacetInstance fid="[:RuntimeParams.FacetIDName:]" Name="TQL" Type="SffTqlPolicyFacet">
   <OnActivate>
      ....
      <NewFacetInstance name="tqlpbdl" type="SffBdlFacet"/>
   </OnActivate>
</NewFacetInstance>

Creating TQL Policies

Creating TQL Policy is a simple matter of issuing TQL Save (or Create) query on the TqlPolicy model.

Simple Create TQL Policy
<Query>
    <Save nested="*">
      <TqlPolicy>
        <Trigger>
          <Light.state eq="on"/>
        </Trigger>
        <Action>
          <Log message="Alert, Light is on!"/>
        </Action>
      </TqlPolicy>
    </Save>
  </Query>
  • Note nested=”*” parameter so trigger and action instances are automatically created. 

  • You can have any number of [different] policy instances. You might consider to define some unique constrains on the policy itself (e.g. unique=”Trigger,Action”) to eliminate duplicates if any, depending on your application needs.

  • TQL Policies can be saved as simple Query files as part of your project within tql folder.

  • Triggers are automatically compiled and deployed as Behaviors to given BDL facet whenever new trigger is created (i.e. new TriggerId detected) and removed when trigger instances are deleted. Engine uses TriggerId[s] to manage its internal trigger registry. Apparently this only works when engine is running. Therefore when the engine is restarted the triggers must be reactivated (see below).


Using Time Constraints

Think of time constraint as system provided virtual attribute (CurrentTime) that can be combined with your own model constraints to form a policy trigger.

  • Fully qualified name of CurrentTime is: Atomiton.TqlSystem.System.CurrentTime
  • Now if you set State attribute of any Light model to on and manage to do it during first 15 seconds of current minute, you’ll see the message in the log.

  • Here we combine two constraints: First, we require that Light.state was updated to “on” and second, we require that the update happened within first 15 seconds of a current minute by providing time constraint. Syntax-vise, we simply pretend that current time.


  • As usual, unless you have your own CurrentTime attribute somewhere, qualifying namespaces can be omitted. It should be also obvious that CurrentTime can be only $Updated() (which is default), but not $Created() nor $Deleted(). Trigger compiler will allow such definitions, but, obviously, this will never happen.

Scheduler Time format Vs Time Constraint Syntax:

  • Note that now we’re not just talking about scheduling where you’d be only interested in [start] time points, but actual time intervals where we have both interval start and interval end time points, thus we use “#15sec” interval length syntax.

Time Constraints
<Query>
    <Save nested="*">
      <TqlPolicy>
        <Trigger>
          <CurrentTime in="[0sec../1min#15sec]"/> <!-- first 15sec of every minute -->
          <Light.state eq="on"/>
        </Trigger>
        <Action>
          <Log message="Alert, Light is on!"/>
        </Action>
      </TqlPolicy>
    </Save>
  </Query>


ReActivating Policy Triggers

A-Stack uses TriggerId[s] to manage its internal trigger registry. The register is stored as temporary cache when engine is running. Therefore when the engine is restarted the triggers must be reactivated. Reactivating triggers is a simple matter of issuing a ReactivateTriggers query.

Reactivate Triggers
<ReactivateTriggers>
    <Find>
      <TqlPolicyTrigger not.TriggerId=””/>
    </Find>
  </ReactivateTriggers>

Find All Active Triggers

It is possible to find all the active triggers by doing a query on the workflow facet associated with the TqlPolicy. The response will be true if the trigger is active.

DeActivate Triggers
<Query>
 <DoRequest Target="PolicyFacetId.bldFacetId">
 <SetResponseData>
   <Key>Message.Value.IsTriggerActive</Key>
   <Value>
    [:$IsDefined($FacetData.wid.<trigerId>):] <!-- You can query the Trigger Id by doing a <Find nested="*"><TqlPolicy><PolicyId ne=""/></TqlPolicy><Find>-->
   </Value>
 </SetResponseData>
</Query>

DeActivating Policy Triggers

You can use <DeactivateTriggers/> to temporarily disable a bunch of triggers at any time and reactivate them later without making any changes to the DB albeit. From an end user perspective Deactivating a trigger is nothing but deleting a bunch of policies and then recreated them.

DeActivate Triggers
<DeactivateTriggers/>
(OR)
<DeactivateTriggers>
    <Find>
      <TqlPolicyTrigger not.TriggerId=””/>
    </Find>
</DeactivateTriggers>

Time Constraint Policies

  1. Stand-alone timer policies always execute their action, regardless wether the trigger query returned a result.
  2. If trigger query returned a result, a $Timer variable will be added to each result set. If not, there will be a default result with just $Timer in it. Presence of $Timer inside $ContextData.$TriggerArgument can be used to distinguish between timer triggering or TqlReport triggering.
  3. If there is a non-empty query in a timer Trigger, and you do not care about query result in your timer policy, it is equivalent to deploying 2 policies: one just for time, and the other for modification.

Example: Trigger a Policy every 10 seconds

Trigger Policy Every 10 Seconds
<TqlPolicy label="p1c-t-1">
  <Trigger label="tgr-t-1">
    <CurrentTime in="[0../10sec]"/>
  </Trigger>
  <Action label="act-t-1">
    <Log format="Time Trigger:%n%s" target="$ContextData.$TriggerArgument"/>
  </Action>
</TqlPolicy>

Key Points about TQL Policy

  • While composing complex triggers, it is imperative to understand that all involved attributes have to be created/updated/deleted at the same time (i.e. be present in the same internal TQL report) in order to be submitted for the same trigger evaluation. Time constraints if present have nothing to do with that! For example in trigger

    TQL Policy Constraints Notes
    <Trigger>
      <CurrentTime in="[0sec../1min#15sec]"/>
      <Light.color value="$Updated()" not.eq="red"/>
      <Light.state value="$Updated()" eq="false"/>
    </Trigger>

    Both color and state must be updated at the same time (in one request) in order to trigger the policy. Current time constraint limits the time interval when it may happen, but in no way means that you can update color at 1st  second of the current minute and state at 14th second in a different request. In other words, each trigger deals with a single moment in time even though the position of that moment on the timeline can be constrained.


  • Currently, any attributes used in a trigger, but not present in internal update report will be assigned null values. That is, in case when only state attribute is updated in above trigger, color will be given null value for trigger evaluation. This effectively represents “not modified” condition.

  • All usual <AND>, <OR>, <NOT> logic can be used with triggers the same way they can be used in Find


  • Using SID as Attribute Constraint: A trigger event on a model’s SID means an event on the whole instance. That is <Light.LightId value=”$Deleted()”/> will trigger upon executing DeleteAll on the Light instance. You can impose any other conditions on SID as on any other regular attribute. Obviously, SIDs themselves can only be $Created() or $Deleted(), never $Updated(), but they can be used to impose conditions on SID itself during other attribute updates (i.e. make policy specific to a particular instance, for example). So in this case the meaning of update is, again, the whole instance (i.e. one or more attributes of an instance were updated).