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

Don’t take it like „shut up or contribute“, take it like „Do not demand, but contribute“.

You can report issue or open feature request and everyone will be fine, but don’t take to right to judge something like:

4 Likes

Frankly I didn’t catch the whole story as I haven’t been involved before.
So there’s a problem with some Blockly rules if on Raspi but only on 32 bit and we all shall upgrade Raspi OS to 64bit because GraalVM only works fine with that ?
Given most parts of native Raspi OS are still 32bit, that sounds a little sketchy to me and not as if that’s the last word on this.

For one, this will not affect many people (some Blockly users only).
Second, there’s 64bit openHABian available so anyone really affected can just reflash his box with that and reimport his config.
So real impact is sparse and a fix is available. I’d say that on global (all-OH) level, that is not a major nuisance. I didn’t catch what the root cause is but I have full trust in @florian-h05 and @stefan.hoehn that they’ll keep trying to identify and fix it.
So it’s not a sufficient let alone good reason to change the overall recommendation.
You might want to amend it with some hint on that for the time being.

1 Like

No, all rules using Javascript with the helper library.
But it’s true that it’s too early to give a clear recommentation. It’s more my current personal conclusion to switch to 64bit as I’m just installing a new system for OH4 anyway.

Blockly “compiles” to ECMA Script 2022, which is run by the JavaScript Scripting (GraalJS) add-on.
GraalVM itself does not support 32-bit, however GraalJS is running on 32-bit up to some version (in fact keeping 32-bit support is holding me back from upgrading GraalJS to a newer version).

With Blockly switching from NashornJS to GraalJS, several users reported that the initial loading of scripts takes really long, which is because the helper library is injected into the script.
Therefore this issue affects all JS Scripting and Blockly users (as @Larsen already said), which of course is not all openHAB users.
Running on 64-bit, no matter whether on GraalVM or OpenJDK/Zulu, seems to really improve the performance of GraalJS (thx to @stefan.hoehn for doing some tests, see [jsscripting] GraalVM's optimised compiler or find another solution · Issue #15600 · openhab/openhab-addons · GitHub).

Given the fact that Jython might go away at some point in time and Rules DSL is IMO outdated (@rlkoshak if you have a better idea how to word this, this idea is really welcome), I feel that both JS Scripting and Ruby Scripting are promoted as go-to solutions when creating new rules.

After veryfying the performance difference between 32-bit and 64-bit and measuring the RAM usage of 64-bit and having a look at the other possible solutions, I would rediscuss this.
I could also imagine having a split solution, that recommends different OSes for different models.

BTW: 64-bit would also allow the installation of InfluxDB 2 on Raspberry Pi.

So only initial loading takes longer on standard openHABian, not execution? That has been true for rules DSL ever, too.
I still would not call that a major nuisance to justify a new general recommendation.
Remember this is only on startup. That
should be neligible on a system designed to run 24x7. I also keep wondering if the statements on GraalVM are fully correct and final. Cannot believe they retroactively drop support for the still most common Raspi setup.
As said you could complement the docs with a statement to try 64bit if on 4+ GB but I would not be recommending that as of today.

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?