Using an HTTP Connection Pool in Actions with Node18 vs Node22

Overview

This article will cover the differences in approach for using a connection pool in Actions with a keep-alive parameter with Node18 and Node22, with the goal of improving overall performance by establishing a TCP connection to an external API, and avoiding having to go through DNS on each request to the same API in a given Action for a single login.

Applies To

  • Actions

Solution

When using Node18, connection pooling will actually add to the overall execution time. The reason is that module-level code will be re-evaluated on every request.

Considering this example:

const http = require('node:http');

const agent = new http.Agent({ keepAlive: true });

exports.onExecutePostLogin = async (event, api) => {

 // Use the agent

};

In Node 18, every execution will get a new agent. The connection pooling will not work and will actually create more work. This is because a keep-alive agent is created and then never used it again. It will leak connections and memory until the TCP keep-alive allows the socket to be destroyed and then the agent can be garbage-cleaned.

In Node 22, where we have ‘Instance Caching’ enabled, the agent instance will be the same object across many requests and can be used for connection pooling.

When using Node18, an alternative would be this:

const http = require('node:http');

// Example key that is designed to avoid collisions since
// we are setting a global mutable variable.

const agentKey = Symbol.for('my http agent');

let agent = globalThis[agentKey];

if (!agent) {

 agent = new http.Agent({ keepAlive: true });

 globalThis[agentKey] = agent;

}


exports.onExecutePostLogin = async (event, api) => {

 // Use the agent

};