Homeyscript memoryleaks

FWIW, I just tested a script that creates 100K promises that are left dangling after the script returns, and it doesn’t seem to faze Homeyscript at all:

for (let i = 0; i < 100000; i++) {
  wait(10000).then(() => {});
}

The promises themselves take up memory (as expected), but once they are fulfilled (after 10 seconds) the memory is freed.

If your scripts are heavy on API calls to other apps, the issue may be related to that instead of to promises specifically. When you await, you’re waiting for the promise to be fulfilled (or rejected) before continuing (basically serializing the calls), whereas with using .then(), the async operations are parallellized, which might be the actual issue.

Still no response from Athom…

.
Just an example, the script below is ofcourse not to call every x secs, but it’s still easy to kill the Hscript app.

The script below returns its data in ~17secs.
When I call this script from a flow, once in every 10secs, Hscript RAM increases from ~18MB to 60 - 65MB, and stays online.
If I call it every second, Hscript RAM increases to ~75MB, and then the Hscript app gets killed pretty quick.


Script

// FindOrphanedLogicsVars.js
//
// Script to find Homey Logics orphaned Variables (by RonnyW)

// Adjusted somewhat, when called from a flow, it returns the output in a Variable (PD)

const flows = await Homey.flow.getFlows();
const logicVars = await Homey.logic.getVariables();
let flowMatches;
let varUsed;
let position = [];
let results2Var; // To be able to build a string to output the results to 
// a variable [OrphanedLogicsVars], for usage in flows
results2Var = ("*** Orphaned Logics Variables found ***\n\n script: FindOrphanedLogicsVars.js\n\n"); 

for (var iLogicVar in logicVars){
results2Var = results2Var + "*VARIABLE*: ["+logicVars[iLogicVar].name + "]\n";
let logicVar = logicVars[iLogicVar].name;
let logicVarId = logicVars[iLogicVar].id;
varUsed = false;

for (var iFlow in flows){
flowMatches = false;
position = [];

// Search for variable name in Triggers
if (flows[iFlow].trigger.uri == "homey:manager:logic"){
for (var iArgs in flows[iFlow].trigger.args){
if ( flows[iFlow].trigger.args[iArgs].name == logicVar
|| flows[iFlow].trigger.args[iArgs].id == logicVarId){
position.push("Trigger card (IF)");
flowMatches = true;
}
}
}

// Search for variable name in Condition cards
for(var iCond in flows[iFlow].conditions){
if ( flows[iFlow].conditions[iCond] &&
flows[iFlow].conditions[iCond].uri == "homey:manager:logic"){
for (var iArgs in flows[iFlow].conditions[iCond].args){
if ( flows[iFlow].conditions[iCond].args[iArgs].name == logicVar
|| flows[iFlow].conditions[iCond].args[iArgs].id == logicVarId ){
position.push("Condition (AND) - direct reference");
flowMatches = true;
}
}
}

for (var iArgs in flows[iFlow].conditions[iCond].args){
if ( flows[iFlow].conditions[iCond].args[iArgs] &&
JSON.stringify( flows[iFlow].conditions[iCond].args[iArgs] ).indexOf( logicVarId ) > 0 &&
JSON.stringify( flows[iFlow].conditions[iCond].args[iArgs] ).indexOf( "homey:manager:logic" ) > 0 ){
position.push("Condition (AND) - indirect reference (Tag)");
flowMatches = true;
}
}
}

// Search for variable name in Action cards
for(var iAct in flows[iFlow].actions){
if ( flows[iFlow].actions[iAct] &&
flows[iFlow].actions[iAct].uri == "homey:manager:logic"){
for (var iArgs in flows[iFlow].actions[iAct].args){
if ( flows[iFlow].actions[iAct].args[iArgs].name == logicVar
|| flows[iFlow].actions[iAct].args[iArgs].id == logicVarId ){
position.push("Action (THEN) - direct reference");
flowMatches = true;
}
}
}

for (var iArgs in flows[iFlow].actions[iAct].args){
if ( flows[iFlow].actions[iAct].args[iArgs] &&
JSON.stringify( flows[iFlow].actions[iAct].args[iArgs] ).indexOf( logicVarId ) > 0 &&
JSON.stringify( flows[iFlow].actions[iAct].args[iArgs] ).indexOf( "homey:manager:logic" ) > 0 ){
position.push("Action (THEN) - indirect reference (Tag)");
flowMatches = true;
}
}
}

// Results of Flows found:
if (flowMatches == true){
varUsed = true;
}
}
if (!varUsed){
results2Var = results2Var +"** NOT FOUND IN FLOWS ** \n\n";
} 
}
// Output to a Hscript StringVariable [OrphanedLogicsVars]
await tag( "OrphanedLogicsVars", results2Var );

// Output (just test to view the result data in 'results2Var')
// console.log(results2Var);
return(true);


Doesn’t it make sense that if you have a script that takes 17 seconds to run and start it every second (or even every 10 seconds), the number of running script instances will pile up because you’re restarting them faster than they can finish?

Also, your script is already using await everywhere, so even if there’s an actual memory leak in it it’s not caused by promises.

2 Likes

Thanks for explaining Robert.
So, this way of killing the app is in the category “rtfm” and not an app error.