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

Kinda what I was thinking

remember this? (circa 2018) I know you remember Markus, you posted in it

Im not sure, I am only a stupid user with extended knowings but the last comment remember on this Openhab 4.0.x rules need a lot of memory - #99 by Wikibear That is exectly why I’m running now a Pi 4 with 4GB. I have roughly 95 rules and Openhab needs more memory before upgrading from 3.4 to 4. With each rule an my system was overloaded. I mean that a raspberry Pi 3 isn’t optimal if you have so many rules and byside other services like zigbee2mqtt, zigbee Broker, Grafana and InfluxDB. The OS Swap most, because 1GB Ram isn’t enough. At the moment my memory goes up with other services to 1,7GB RAM on Rasp 4.

I want to share only me experience. After switching to a Pi 4 Openhab is much better. Of there is a power user like me, Raspberry Pi 4 is recommend instead of a Raspberry Pi 3.

We come over exact to this point that 24/7 use isn’t really a problem, only on low systems get problems with rules caching. Yes, and it’s pain in a** if you develop a Blockly rule… Every test need 20 sec and that drive me totally crazy. I will say, I that new guys to openhab aren’t really happy if the need so many time for developing a rule…

1 Like

In general a 20 second delay for a first run is neglectable. That is true for most rules but there are some rules where such a delay is not wanted such as door bell to play a sound, etc.
I just want to elaborate what @Wikibear pointed out:
If you professionals write a new rule or make changes for an existing rule you probably write the code, make one test, have one error you fix it and you are done.

All the others have tens of errors and the log file is showing up just a few of them. You have a LOT of iterations of fixing code and running the script. Sometimes you just forgotten a “;”. After you corrected the code another simple error where you missed closing parenthesis pops up. After your code is syntactically correct you run your script and realize that the result is not the one you expect. So you need to add/change your code again and very often you realize that your first approach is too complicated and have a new idea to make it better. So you start again from the begining or somewhere inbetween.
In THIS particular case it is NOT neglectible, it is really an annoying situation to wait in half an hour of code fixing 30 times 20 seconds. This is no complaint, I just wanted to give you the view from non-professional coders.

2 Likes

I think that’s premature. We don’t know yet if what is being proposed will improve the situation and if so, by how much. We have pretty good indications that it will work but we shouldn’t go about changing long made recommendations until we have gathered all the necessary info.

If moving the GraalVM JRE does improve things, we will need coordinate a number of changes across a number of repos.

When we know, we will make this known somehow.

Just for clarity, the “there” is the issue @florian-h05 posted above.

There is an issue with all JS Scripting rules where the first run can take a significant amount of time because of the injection of the helper library (any libraries really). There is some evidence that the rules themselves run slower than the other rules languages as well.

The current hypothesis is that if we run on GraalVM instead of OpenJDK we should see a significant improvement in both of these, definitely the first. However, GraalVM does not have compiled versions for 32-bit ARM.

I would say it’s a sizable minory, if not the outright majority.

No, that’s how I’d put it and I’ve been promoting those two that way for some time now, especially warning against the use of Jython.

It is also when editing. If it takes 20-30 seconds every time you hit save for that first execution of the rule to start, that’s more than just a minor annoyance.

You can compile a 32-bit version yourself if you want, I think. I never tried it and don’t know if there is some library they depend on that only has 64-bit support. But as far as I can tell, GraalVM has never released 32-bit ARM builds. It’s not retroactive, they’ve just never supported it in the first place. I don’t think that SBCs like RPi is really on their radar as a typical user.

4 Likes

@florian-h05 , @rlkoshak , what do you think of the following “workaround”, which helps at least to “solve” the first run delay after a restart and after changing a rule. These delays are annoying especially for sensitive rules like play door bell sound or a MainUI widget which starts a runRule action, etc.
(However, this workaround does not solve the 20 seconds delay while editing/saving a rule).
The idea is as follows:

  • tag all “sensitive” rules
  • start a rule at systemLevel 80 which does a runRule() of each rule tagged:
var RuleManager = osgi.getService("org.openhab.core.automation.RuleManager");
var javaRules = osgi.getService("org.openhab.core.automation.RuleRegistry").getByTag("cache");
var jsRules = utils.javaSetToJsArray(javaRules);

jsRules.forEach(r => {
  if (RuleManager.isEnabled(r.getUID()) {
    console.log("Info: Rule",r.getName(),"(", r.getUID(),") will be cached");
      rules.runRule(r.getUID(), {'key0': r.getUID()});
  }
});

As we do not really want that the code in our tagged rule is executed we just need to make sure the rule knows it is started just for caching reason:

if ((typeof key0 !== 'undefined') && (key0 == ruleUID)) {
  //do nothing as we just want to enforce a cache
} else {
  //original script goes here
}

For rules which need to be selectively cached there will be a simple list-item widget which dos a runRule action of the edited rule.

Before implementing this I thought better assk experts if they might have concerns or better ideas.

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

Wow, thanks Rich, for this great teaching lesson.
Needed to read it twice to fully understand all your comments.

Very interesting!
I had a look at your rules where you checked this.event in the “But only if” section.
What I don’t understand why this code in the “But only if” section is not working

if(this.event === undefined){
  console.log("Rule aborted");
  false;
} else {
  console.log("Rule ok");
  true;
}

The script is always executed.

How is the rule triggered?

When a rule is triggered manually, the conditions are never applied (which could be a problem with the edited the rule use case now that I think of it). It’s a work around so I don’t imagine it’s ever going to cover all the cases.

I see. I started the rule manually and thought that “But only if” gets executed. Obviously that’s not the case. Would have been a great option because the original code would remain unchanged.
Anyway. I like this.event approach and will now change my rules accordingly.

I need to install this software:

and not this one:

correct?

We don’t usually recommend Oracle Java because of their onerous licensing but in general the “correct” one is going to be what ever Java 17 implementation will run on your CPU (ARM, Intel, AMD, etc.) and operating system. To run 64-bit Java you must have a 64-bit processor and 64-bit OS.

1 Like

I wanted to do some test like Stefan did.
Assume that I will choose the correct package for my CPU and architecture, I just wanted to double check I choose the correct jdk package (GraalVM JDK17 vs. JDK17).

To do that test you’ll need an RPi and at least two SD cards or two RPis. The problem is most pronounced on RPis.

One will need to have a 32-bit Raspberry Pi OS installed with, of course, a 32-bit JDK. The other will have the 64-bit Raspberry Pi OS installed and then there’s two tests, one with just a 64-bit JDK of any variety and one with GraalVM which only comes in 64-bit flavors.

1 Like

Oliver, I use sdkman on my RPI (as Rich mentioned, it is a 64bit version of openhabian) to install and switch between the installed sdks.

In general you use

sdk list java

to view all possible versions you can install.

This is how you can list your installed sdks and see which ones I used:

sdk list java | grep install
| >>> | 17.0.7       | graalce | installed  | 17.0.7-graalce
|     | 17.0.7       | zulu    | installed  | 17.0.7-zulu

You can then just do

sdk install java 17.0.7-graalce

and as Rich said: Don’t use Oracle due to the license implications.
Hope that helps a bit.

1 Like

I have been switching from openHABian 32Bit to 64bit and imported my backup (Openhabian 4.03 @ Raspberry Pi 4B 8GB)

Unfortunately, there is no change in behaviour. My simple rules for switching and dimming light through a Zigbee Device are delayed by roughly 10 seconds on first execution and after some hours of not executing. Is there any other solution yet?

So far the only solutions are to run with a 63-bit OpenJDK or GraalVM. Are you sure you installed a 64-bit Java?

Hi! This is a screenshot of my openHABian installation:

If I understand the context correctly, and the explanation given previously in this thread (lots of disclaimers here :slight_smile:), to get better performance you would need to replace OpenJDK with GraalVM. And GraalVM only supports 64 bit. So just running OpenJDK 64 bit will not improve anything.

Based on the experiments run thus far, 64-bit OpenJDK shows similar if not better improvement than GraalVM.

That’s good news! Thanks for testing and reporting that.