Problems using javascript cache

Hi,
I just started to copy my scripts to the new ECMA-Script Version.
With a little effort everything works fine.
@rlkoshak told me than to use the javascript cach instead of the way with this to save variables over several script launches.

To start with this I tried to run the example from the Docs

let counter = cache.get("counter");
console.log("Counter",counter);

if(counter == null){
     counter = {times: 0};
     cache.put("counter", counter);
}
console.log("Count",counter.times++);

I have a simple TestSwitch on a page that trigger this rule ( condition on Change).

But if I look in the logs I received

Script execution of rule with UID ‘681ac0a9e8’ failed: java.lang.IllegalStateException: The Context is already closed.

Did anyone has an idea whats going wrong?

That’s the rule UID for the rule using the cache? If not, what’s in that rule?

This is a UI based rule? If so, you can’t use let or const in the top level of the script. The script gets reused and the second time it runs it will try to create the counter variable again and can’t because it already exists. It’s a quirk in how OH handles rules.

Yes that is the id from the rule using the cache.
The rule does nothing more than you see above.
the rule is created from the ui.
The rule is triggerd from a test item, a switch, an was activated with the on change…

The only thing I want to see how the cache works.

I know the problem with the recreation, but I think with the if==null takes care about creating it only one time.

So
First run create the variable and entry in cache.
Every further run, increase the counter entry.

No because it would never get there. The first time the rule runs it will work fine. The second time it will see that counter already exists and fail because you can’t redeclare a variable that was declared using let.

But even if I remove the let, it will not work

counter = cache.get("counter");
console.log("Counter",counter);

if(counter == null){
     counter = {times: 0};
     cache.put("counter", counter);
}
console.log("Count",counter.times++);

I still receive

2022-08-08 19:24:27.744 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID '681ac0a9e8' failed: java.lang.IllegalStateException: The Context is already closed.

Well, you still have to declare it as a var or declare it as part of this.

I just ran this very slightly modified version.

var key = ruleUID+"_counter";
var counter = cache.get(key);
console.log("Counter", counter);

if(counter === null){
     counter = {times: 0};
     cache.put(key, counter);
}
console.log("Count",counter.times++);

Here’s the result

2022-08-08 13:40:48.928 [INFO ] [openhab.automation.script.scratchpad] - Running scratchpad.
2022-08-08 13:40:48.944 [INFO ] [nhab.automation.script.ui.scratchpad] - Counter null
2022-08-08 13:40:48.947 [INFO ] [nhab.automation.script.ui.scratchpad] - Count 0
2022-08-08 13:40:57.619 [INFO ] [openhab.automation.script.scratchpad] - Running scratchpad.
2022-08-08 13:40:57.652 [INFO ] [nhab.automation.script.ui.scratchpad] - Counter {
  "times": 1
}
2022-08-08 13:40:57.653 [INFO ] [nhab.automation.script.ui.scratchpad] - Count 1
2022-08-08 13:41:04.765 [INFO ] [openhab.automation.script.scratchpad] - Running scratchpad.
2022-08-08 13:41:04.769 [INFO ] [nhab.automation.script.ui.scratchpad] - Counter {
  "times": 2
}
2022-08-08 13:41:04.771 [INFO ] [nhab.automation.script.ui.scratchpad] - Count 2

The biggest change was adding the ruleUID to the key so this doesn’t interact with anything I have in the cache from my other rules, I use a variable for the key, and most importantly, I define my variables using var.

I’m not sure what else could be wrong.

Edit: I just thought of something and ran a test and confirmed it.

You must clear the cache variable every time you edit and save your rule if you put anything beyond a primitive in the cache. If you put an Object into the cache (a dict is being used here) only a reference to the Object is saved. When the rule gets destroyed and recreated after an edit, the Object that reference points to is gone so you can’t use it any more.

To clear a variable from the cache, cache(<key>, null);.

3 Likes

Hi ,
on my side it works in the same way as you described it. So thanks.

But the need to clear the cache entry in case of reload or save of the script…I find it a little bit confusing.
I did not find a way to do it in the same script. I found no event or another think like (‘rule saved’ or
‘rule reloaded’ ) to use in the script.

I just created an other script, which only does this cleanup.
So it works, but everythim during development I need to remember this other script to run.

Did you now a more elegant way?

I don’t know about elegant but I will usually add the line to clear the entry from the cache in the same script, but commented out. Then when I modify the rule I’ll uncomment that value out, run it, then comment the line back out again. Obviously the rest of the rule needs to no put something back into the cache after the line that clears it out.

I try to use the cache as little as possible so I don’t have many rules that this impacts.

Ok, that’s angoog idea to use it not very often.
But how will you handle such use cases?

Example.
If my heating is on and I leave my home, openhab detects that my handy is no more in the wlan.
In that case I switch of the heating.
The old heating state, so energy heating or normal heating I save in the cache, or before in the this variable.

If I come back home I will restore the heating value to the previous saved value.

For such things I use the cache.

You probably should use an Item for that sort of thing. As an Item with restoreOnStartup, the value will survive a reboot of OH and your rules will operate correctly despite the restart of OH. In the cache the value will be wiped out during the reboot.

Consider the case that OH crashed and rebooted while you were away. When you return your rule to restore the heating values will fail because those saved values are gone. Of course you could add a bunch of code to handle that, but why not just make sure the values will always be there?

I thought about the solution with using an item at the beginning.
But than I read in the forum a thread from you with using this in. Kombination with a timer or so.

Then I thought, ok that is the better way, because an item is something different, with a different use case.

But when I see all the problems belonging to this or to the cache, maybe the item is the better solution.

Thanks for you’re opinion.

There are always different use cases and different concerns that drive a decision to use one approach or another. You can’t always simply apply the same advice to all cases.

Additional approaches could be to use Persistence and lastState, Item metadata to store the old value, writing the values to a file and reading them back, etc. Each has their pluses and minuses and those pluses and minuses depend on exactly what you are trying to do.