Node.js web server recommendations

Introduction: main principles of setup

The Sage X3 web server architecture is based on node.js instances that are load balanced. Each node.js process can manage pages for several users.

The Syracuse load balancer distributes user sessions over the different node.js processes. The distribution is based on an algorithm that considers the response time and occupation rate (number of sessions) of every node.js process.

The load balancer will also:

It is critical to size the number of nodes and to tune their CPU and memory consumption to get the best performance. node.js is a standard framework that works in asynchronous mode. It is therefore difficult to accurately estimate the number of node.js processes and to size them for a customer instance without extensive analysis.

The sizing requirement differs according to the type of page and functions the users run. The heaviest consumption is encountered with classic pages containing grids with a large number of lines (over 1,000).

The guidelines in this document are a starting point but can be modified on site according to observation, if the result is not optimal.

The configuration of node.js is based on three elements:

Number of node.js processes

The number of node processes depends on the maximum number of session requests during peak activity.

You can use a set of simple rules to initially size a Sage Syracuse (Web) server:

The number of node.js processes can be set up through the Administration menu: Administration > Servers > Hosts, mainly through the Number of child processes setting. You do not need to restart the server when increasing the number of child processes, but when decreasing them you need to make sure you are the only user connected to the application.

Some recommendations can be given to the end users to reduce memory and CPU pressure on the node.js processes:

Sizing of node.js processes

A default Syracuse installation has default sizing settings that define the memory consumption of each node.js process, but you can modify them and also use advanced parameters defined in the nodelocal.js configuration file.

The configuration file is located on the Syracuse server (..\Syracuse\bin\nodelocal.js) and contains default parameters settings. The nodelocal.js file is never modified during an update and must always be set up manually. The modification will only be effective when you restart your Syracuse server.

Warning: It is recommended to back-up the file before any modification. Incorrect JSON syntaxes in nodelocal.js will prevent the Syracuse server from starting.

The default nodelocal.js file supplied at installation does not contain any of the advanced sizing parameters but experience shows that it might be necessary to change default values to cover most heavy activity classic page users. The table below lists the settings and their recommended values.

Three parameters are related to the heap memory consumption (i.e. the dynamic allocated memory) of node. The heap memory includes the objects used by node.js and the ones that have been allocated but are not used anymore. Periodically, the garbage collector will make the no more used memory available again. These parameters defined in MB are the following:

As the load balancer will kill the node.js process if the memory exceeds memoryThreshold2, there is no significant advantage to make max_old_space_size much bigger than memoryThreshold2. The higher this parameter is, the less frequently the node will consider cleaning the allocated but not yet used memory (in other terms, running the garbage collector). Therefore, if there is a difference between memoryThreshold2 and max_old_space_size, it might only enhance the performance of the running nodes by decreasing the number of times the garbage collector runs.

Additional parameters are used to manage the load balancer behavior:

The process used by the load balancer to manage the process can be summarized by the following flowchart:

Note: from version 12, it will also be possible to define memoryThreshold1 and memoryThreshold2 as a percentage of max_old_space_size with the following syntax: memoryThreshold1="80%". If no values are given for memoryThreshold1 and memoryThreshold2, they will be defined by default as 80% and 95% of max_old_space_size.

The recommended values for these advanced parameters are as follows:

Value recommended for normal/medium usage (Inquiries < 1,000 lines, no large Excel exports)Value recommended with large classic screens (Inquiries > 1,000 lines, large Excel exports, multiple tabs with large screens…)
memoryThreshold12,500 MB4,000 MB
memoryThreshold23,000 MB5,000 MB
childPingStatusTimeout30,000 ms
childMaxTimeoutDelay30 min
max_old_space_sizeAt least the value of memoryThreshold2 (an increment of approximately 10% can be added) 3,500 MB or 5,500 MB for the example above
maxRestrictedChildren
  • Recommended general value: maxRestrictedChildren = number of node child processes.
  • Caution: this has to be adjusted depending on user activity and on server resources. For example, if your users rarely or never use large inquiries, then you can safely assume they will never need that many restricted nodes.In general, for a small number of node processes (e.g. 4, 5) you can apply this formula. For larger numbers, you need to evaluate user activity and adjust. Please contact Sage Support in case you have any doubt.

nodelocal.js example

Here is an extract of a configuration that can be adapted:

"searchEngine": { "hostname": "mysearchserver","port": 9600},"x3fusion": {"records": {"dumpPath": "C:\\Sage\\SAFEX3\\MYSYRSERV/Syracuse/cache/_cvg___USERNAME__"},},"session": {"timeout": 20,"checkInterval": 60,"auth": "basic"},"nanny": {// Number of nodes in restricted mode before killing the next node turning in restricted mode:// DefaultValue = 10; most of the time, 10 nodes are too much and allocate too much memory// In this example there are 5 nodes."maxRestrictedChildren": 5,// try to connect child processes every 'childPingStatusPolling' milliseconds.// Load balancer will measure the response time and consider this for load balancing.// This is a sliding mean, it is computed as follows:// the newest value will be considered by (100-'childSlidingMean') percent, the previous computed// value by 'childSlidingMean' percent. When a child process takes longer than// 'childPingStatusTimeout'milliseconds for the ping response, it will be turned into // restricted mode.// Default value: 60000 (milliseconds)"childPingStatusPolling": 60000,// Default value: 20 (percents)"childSlidingMean": 20,// Default value: 30000 (milliseconds)"childPingStatusTimeout": 30000,// Max delay for a child to stay in a consecutive timeout state. // If this period of time is exceeded, the process is killed.// Default value: 30 (minutes)"childMaxTimeoutDelay": 30// waiting time (milliseconds) during load balancing to obtain results of other processes// Default value: 600"balancingWaitTime": 600},"system": {// Load balancer will not assign new sessions anymore when the heap usage exceeds this value (in MB)"memoryThreshold1": 2500// Process will be killed during load balancer ping operation when the heap usage exceeds // this value (in MB)"memoryThreshold2": 3000},"hosting": { // node.js options "nodeOptions": "--max_old_space_size=3500"}}

Server resources

The total memory and CPU capacity on the Syracuse server must be tailored for your settings.
Total memory must be able to accommodate all node processes at their peak usage, and also the additional “no_cleanup” node processes that are allowed by maxRestrictedChildren, which must be taken into account. The maximum memory consumption of a node is defined by max_old_size.

In addition, you must consider the operating system and any other applications running on that machine. Make sure you have enough memory available. Also make sure you have appropriate swap space on the server's disks.

The total number of cores is calculated similarly based on the guidelines in this document. Also make sure you have enough physical cores, proper socket distribution and the proper power management settings (set to 'Performance', and not 'Balanced'). These will be important for the number of vCPUs if you are running a virtualized environment.

Memory consumption examples

Memory consumed by a connection is directly linked to the activity of that session. For example, running a large inquiry or request with 10,000 lines displayed on the screen will cause the related node.js process to consume hundreds of Megabytes holding the full data structure. Syracuse pages do not cause as much memory consumption as paging/caching is used natively. Here are a few typical examples as an illustration:

Sage X3 management functionDisplaySession's node.js memory consumption
Account inquiry (CONSCPT): 10,000 lines500 lines per page, 20 pages~ 600 MB
Manual matching (LETTRAGE): 30,000 lines15 lines per page, 2,000 pages~ 950 MB
Manual matching (LETTRAGE): 30,000 lines500 lines per page, 20 pages~ 850 MB

Calculation formulas

You will find below a set of formulas that you can use as a starting base to set up an initial architecture to work with.

Caution: these are only initial indications, you will have to evaluate and tune the parameters according to your customer’s use cases.

Assuming:

Then:

Sage legal disclaimer

The information contained in this guide are for general guidance only and are subject to change without notice. The sizing principles explained are intended to give you an indication on how to size the web server. The result may vary depending on the configuration and the use cases.

If you need specific recommendations for dedicated cases, we recommend a collaboration with Sage experts.