BPM, Workflow, and Case

A Note on Performance of JavaScript in IBM Business Automation Workflow

By Georg Sander posted Mon January 11, 2021 09:54 AM

  

Authors: Andrea Baader & Georg Sander

JavaScript in IBM BAW

IBM Cloud Pak for Automation is IBM’s Cloud solution for all automation needs. IBM Business Automation Workflow, which is part of IBM Cloud Pak for Automation, provides automated workflow capabilities for business process management (BPM). One of the most powerful features is the usage of JavaScript in tasks, team retrieval services and team filter services, because it allows to express complex calculations in a language that is well known in all developer communities.

In a process or service, JavaScript is executed at the server side and not within a browser, hence it does not expose the full browser JavaScript API. For instance there is no API to manipulate a browser dom tree, simply because on the server side, there is no dom tree. On the other hand, it provides JavaScript access to the business process objects such as services, tasks, teams, users, roles and to input and output variables of services and tasks. For instance, the JavaScript TWUser class corresponds to a business user, and the JavaScript TWRole class corresponds to a role (e.g. an LDAP group). The convenience of the JavaScript API lets the process developer easily forget that the JavaScript objects are just thin wrappers that internally call the corresponding functionality on the real server BPM objects. These will sometimes trigger heavy calculations for obtaining a result.

A common performance pitfall

It often happens that we need to iterate over all subobjects associated with a BPM object in order to do certain calculations. An example is a team filter service that needs to iterate over all users of a role. You might be tempted to formulate it as follows:

var role = ...
for (var i = 0; i < role.users.listLength; i++) {
var username = role.users[i].name;
...
}

On the first look, there is nothing suspicious with this code. It seems to access the property “users” of the “role” object, and our experience from browser JavaScript code is that property access is fast.

But wait! The JavaScript object TWRole is just a thin wrapper around complex server side code. The code for “role.users” needs to query the LDAP to find those users. LDAP queries need a considerable amount of time. How often will this code query the LDAP if the role consists of 100 users?

The answer is 201 times. The loop condition queries 100 times to iterate over the users and one time to detect the iteration end, and inside the loop body, there are another 100 LDAP accesses. Each access will transfer 100 elements from the LDAP. Hence this code is indeed very slow.

A much faster way to formulate the same loop reduces the execution to only one LDAP access:

var role = ...
var roleUsers = role.users;
for (var i = 0; i < roleUsers.listLength; i++) {
var username = roleUsers[i].name;
...
}

The same performance pitfall applies to “user.roles” (getting roles of a user), “team.users”, “team.allUsers” (getting users of a team), “team.roles” (getting roles of a team) and many other properties. As a rule of thumb, a JavaScript programmer should not assume that a property access is fast inside the BPM system, if the property is an array or complex type. At the end of this article, we include tables for the properties of some important JavaScript classes to explain which properties are fast and which are slow.

Experimental data

We did an experiment to compare the slow code with the fast code. First, we created a group with 100 users in our local LDAP, and a simple process consisting of a single script task:

Script Task to test Javascript performance

The Javascript is:

var role = tw.system.org.findRoleByName(“testgroup1”);
log.info("Start1");
for (var i = 0; i < role.users.listLength; i++){
log.info(role.users[i].name);
}
log.info("Stop1");

log.info("Start2");
var roleUsers = role.users;
for (var j = 0; j < roleUsers.listLength; j++){
log.info(roleUsers[j].name);
}
log.info("Stop2");

In the server logs, we can find the Start and Stop markers. The slow code is between Start1 and Stop1 and the fast code is between Start2 and Stop2. It is noticeable in the logs that role.users does not emit a particular sorting:

[12/3/20 10:29:01:064 CET] 00000164 LoggerScripta I Start1
[12/3/20 10:29:02:053 CET] 00000164 LoggerScripta I testuser66
[12/3/20 10:29:02:086 CET] 00000164 LoggerScripta I testuser99
[12/3/20 10:29:02:110 CET] 00000164 LoggerScripta I testuser23
[… 97 more lines … ]
[12/3/20 10:29:03:651 CET] 00000164 LoggerScripta I Stop1
[12/3/20 10:29:03:651 CET] 00000164 LoggerScripta I Start2
[12/3/20 10:29:03:658 CET] 00000164 LoggerScripta I testuser66
[12/3/20 10:29:03:659 CET] 00000164 LoggerScripta I testuser99
[12/3/20 10:29:03:660 CET] 00000164 LoggerScripta I testuser23
[… 97 more lines … ]
[12/3/20 10:29:03:739 CET] 00000164 LoggerScripta I Stop2

Here, the slow variant of the code needs 2587 ms while the fast variant needs only 88 ms, which is a speed factor of nearly 33 in this particular case. This effect becomes more dramatic when there are more users and it also depends heavily on the speed of the LDAP. Since in our case, the small test LDAP was local to the BPM system, LDAP access in our test was typically faster than when a company wide LDAP is used. That means, in a real production environment, this effect is even more dramatic.

Should method calls on BPM objects always be avoided?

Recently, someone asked us whether it makes sense to avoid a function call and to rather simulate it in JavaScript. A typical example is to test that a user is in a role. This can be done by:

if (user.isInRole(“testrole”)) { … }

or, by using a hash set of al roles of the user, by:

var userRoles = user.roles;
var userRoleSet = {};
for (var j = 0; j < userRoles.listLength; j++) {
userRoleSet[userRoles[j].name] = true;
}
if (userRoleSet.hasOwnProperty(“testrole”)) { … }

Our general recommendation is to use the function call “user.isInRole(…)”. The second variant might only be faster if a huge amount of role tests are done on the same userRoleSet. Again, we created a user that is in 100 roles, and we repeated the role test:

for (var i = 0; i < 10; i++) {
var found = user.isInRole(“testrole”);
}
for (var k = 0; k < 10; k++) {
var found = userRoleSet.hasOwnProperty(“testrole”);
}

The first variant was faster with 10 iterations. This means 10 calls of “user.isInRole(…)” are faster than 10 times the JavaScript simulation on the userRoleSet. The second variant was faster with 1000 iterations (ca 100ms vs ca 400ms), but situations with 1000 role tests on the same user are in real-world processes rather seldom. In the range between 10 and 1000 iterations, it is hard to predict which variant is faster, because it depends on whether the tested role is really found, and how fast database and LDAP are. In this case, we prefer variant 1, since it is shorter and easier to read.

Scriptable Property Access Summary

Here is the performance information we collected for property access of some important JavaScript classes in BPM. Properties are fed from different sources: caches, database, user registry. In most cases, values accessed through a cache are collected fast, but there are some exceptions: even if a property value is collected from a cache, the complexity of the data type can slow down the access. Furthermore, it should be considered that even when values are accessed through a cache, the first access can be slow if the cache is not yet filled.

If cache access is marked as NO, then the values are either obtained from the database or from the user registry (e.g. the LDAP).

TWUser
Property
Fast
Cache access
attributes
NO
YES
fullName
YES
YES
id
YES
YES
isActive
YES
YES
name
YES
YES
participantGroups
NO
NO
roles
NO
NO
savedSearches
NO
NO
scoreboards
NO
NO
teams
NO
NO

TWRole
Property
Fast
Cache access 
allUsers
NO
YES
containerRole
NO
NO
id
YES
YES
managedTeamRoles 
NO
NO
name
YES
YES
roles
NO
YES
teamManagerRole
NO
YES
users
NO
YES

TWParticipantGroups, TWTeam** 
Property
Fast 
Cache access
allUsers
NO
YES
associatedRole 
NO
YES
id
YES
YES
name
YES
YES
processApp
NO
NO
roles
NO
YES
snapshot
NO
NO
users
NO
YES
** Implementation is similar.

Conclusion

JavaScript is a powerful tool for IBM Business Automation Workflow, but developers need to be aware of the special environment used for JavaScript in order to write efficient code. We hope the suggestions mentioned above help to write high quality code without performance bottlenecks.


#Featured-area-1
#Featured-area-1-home
#BusinessAutomationWorkflow(BAW)
#JavaScript

Permalink