Blockly rules after upgrade to OH 4.0.1 very slow on the first run

So that’s what you were working on. :wink:

There are some caveats to this approach. The rules being called need to handle event not being defined (if they are UI rules, I’m not certain for file based rules) or else your log is going to become loaded with errors.

But it seems like it should work.

If you have rules triggered by Thing status changes or Item state changes, this period can be inundated by events. It might be prudent to wait for runlevel 100 for things to calm down, or perhaps even a little bit after (via a timer). I created Delay Start [4.0.0.0;4.9.9.9] for just this reason; it disables “noisy” rules at startup then reenables them sometime after. I had a bunch of sensors that were ponding a rule during startup as they all nearly simultaneously report their new states when the Things come online.

You can use the streaming (or what ever it’s called in JavaScript) to streamline (pun intended) the code here.

Also, you’ll probably want to call the rules twice, once with the flag to consider the rule conditions and again to not consider the conditions. Rule conditions can be scripts too and if you don’t consider them, that code won’t be executed and therefore won’t be cached. But if you do consider them, the script actions might not be executed if the conditions aren’t right and those won’t be cached.

jsRules.filter( rule => RuleManager.isEnabled(rule.getUID()) )
       .forEach( rule => rules.runRule(rule.getUID(), { 'caching': true }, true );
       .forEach( rule => rules.runRule(rule.getUID(), { 'caching': true }, false );

I have too loops there because a rule calling another rule is not thread safe. When the same rule is called too fast I still see the Multithreaded exception. I’m assuming by looping twice there will be enough time between the two calls to avoid that.

Definitely use a more meaningful variable name than "key0".

There is another approach that would also work for manually triggering the rule which is what you’d want to do after modifying it usually. If it’s important the rule run fast that first trigger after startup, it’s still important after modification.

When the rule is triggered, as of OH 4 at least, event is always defined (including time based and system started triggers). If event is undefined that means the rule was triggered by the user pressing the play button or it was called from another rule. Therefore, in the called rule you can use:

if(this.event === undefined) {
  console.info('Caching');
}
else {
}

You can see examples of this approach in many of my rule templates. There I just check the config of the rule and the Items the rule uses when manually triggered. I’d recommend this approach though as it will work both when triggered from a rule and when the rule is run manually.

Though, that won’t help for rules that are normally run this way but that’s a less common use case. Those rules can look for variable passed from the calling rule if it needs to do something different when called from another rule or manually.

Note, the above if statement (or equivalent) needs to be added to all the scripts in a given rule.

I’m not sure this will work for Blockly users though without using the inline script block (or a special Blockly library with a new block deployed to the marketplace too. There is no undefined block so there’s no way to test if event exists. Or maybe there is a way I don’t see?

Maybe instead we could add an “exists” option to the “contextual info” block. That would be useful beyond just this one use case. I might file an issue for that after thinking about it.

It would be best if this were deployed as a rule template. Then end users can just install it and configure it. The only configuration parameter would be the name of the tag so it would be a simple template overall.

As a template it would look something like (note this is a copy/paste/edit, it likely contains typos):

uid: cache-rules
label: Cache Rules
description: Runs rules tagged with the configured tag at system startup to avoid the initial load delay when they are triggered later on.
configDescriptions:
  - name: tag
    type: TEXT
    label: Tag
    required: true
    description: Rules with this tag will be called by this rule at startup.
triggers:
  - id: "1"
    configuration:
      startlevel: 100
    type: core.SystemStartlevelTrigger
conditions: []
actions:
  - inputs: {}
    id: "3"
    configuration:
      type: application/javascript
      script: >-
        var RuleManager = osgi.getService("org.openhab.core.automation.RuleManager");

        var javaRules = osgi.getService("org.openhab.core.automation.RuleRegistry").getByTag("{{ tag }}");

        var jsRules = utils.javaSetToJsArray(javaRules);

        jsRules.filter( rule => RuleManager.isEnabled(rule.getUID()) )
               .forEach( rule => rules.runRule(rule.getUID(), { 'caching': true }, true );
               .forEach( rule => rules.runRule(rule.getUID(), { 'caching': true }, false );
    type: script.ScriptAction
1 Like