Connection sits idle forever on PostgreSQL with Node.js and pg - node.js

We have the following on node-postgres documentation:
// number of milliseconds to wait before timing out when connecting a new client
// by default this is 0 which means no timeout
connectionTimeoutMillis?: int,
And then, a little bit later on the same documentation:
You must call the releaseCallback or client.release (which points to the releaseCallback) when you are finished with a client. If you forget to release the client then your application will quickly exhaust available, idle clients in the pool and all further calls to pool.connect will timeout with an error or hang indefinitely if you have connectionTimeoutMillis configured to 0.
So, I was expecting that if I set connectionTimeoutMillis to 1000, then, after 1s idle, it should automatically release the connection, even if I don't call client.release().
But running the code below it sits idle forever on PostgreSQL:
// test.js
// PostgreSQL v14.2
// Node.js v16.15.1
// node-postgres (pg) v8.7.3
const { Pool } = require('pg')
const pool = new Pool({
user: 'postgres',
password: 'postgres',
host: `localhost`,
port: 5432,
database: 'app_dev',
max: 10,
connectionTimeoutMillis: 1000,
idleTimeoutMillis: 1000
})
;(async function() {
const client = await pool.connect()
const {rows} = await client.query('SELECT NOW()')
console.log(rows[0])
})()
Am I missing something?

I think this is what you are missing
When your app is calling
const client = await pool.connect()
This is where the connectionTimeoutMillis is valid. i.e. it is the amount of time in milliseconds to wait when trying to get a client (a connection) to your PostgresDB. If you set this to 1000ms it means, if I haven't created a new client or returned an existing one from the pool within 1000ms - throw an error. So since you are after obtaining the client - that will not release it or implicitly return your client to the pool as you HAVE the client - therefore there is no waiting on a connection to be established.
The idleTimeoutMillis time refers to the amount of time that the client (connection) can sit in the pool without being called by your application code before being destroyed.
For example, say your pool has a size of 1 client. Your app uses the client and then releases it back to the pool. If your app doesn't request another client for more than 1000ms i.e. the client is idle in the pool for a time equal or greater than the idleTimeoutMillis - it will be destroyed.

Related

SQL Server and NodeJS - Connection Pool availabilty is always zero

I'm trying to initialize a pool of SQL Server connections for my nodejs web application to use. I've set the config to create 10 min connections but when I start the app with the below code. I only have 0 available connections which doesn't allow me to begin any transactions.
Any help would be greatly appreciated!
test.js
const sql = require('mssql');
require('dotenv').config();
const appPool = new sql.ConnectionPool({
user: process.env.DB_USER,
password: process.env.DB_PWD,
database: process.env.DB_NAME,
server: process.env.DB_HOST,
pool: {
min: 10,
max: 100,
acquireTimeoutMillis: 15000,
},
options: {
encrypt: true,
trustServerCertificate: false
}
});
appPool.connect().then(pool => {
console.log(`SERVER: Connected to the db and ${pool.available} connections are available!`);
});
Output
MINGW64 ~/Desktop/React Projects/dummy-project (master)
$ node test.js
SERVER: Connected to the db and 0 connections are available!
node-mssql uses tarn.js for connection pooling. The connections are not created preemptively. Instead, they're created as and when the connections are requested. Once the number of concurrent requests for connections exceed pool.min, the available connections in the pool stays at that level.
So, doing a transaction or running a query shouldn't be a problem, node-mssql will create the connection and fulfil the request.
If you really want to have pool.min connections in your pool, you can write a small script to fire some concurrent queries on your database which will warm up the pool for you.

optimize number of redis connections with a node.js-application

I have a question about redis connections.
I'm developing an app in react native which will use websockets for chat messages. My backend consists of a node.js-app with redis as pubsub mechanism for socket.io.
I'm planning on deploying on heruko. I'm currently on the free hobby plan, which has a limit of 20 connections to redis.
My question now is: how can I optimize my code so that a minimum of connections are used. I'm ofc planning to upgrade my heroku plan once I launch, but then still I want to optimize.
My node.js-code looks like this (simplified):
const Redis = require('ioredis');
const pubClient = new Redis(/* redis url */);
const subClient = new Redis(/* redis url */);
const socketClient = new Redis(/* redis url */);
const io = require('socket.io')(server);
io.on('connection', async (socket) => {
// store socket.id in redis so I can send messages to individual users
// based on the user ID
const userId = socket.handshake.query.userId;
await socketClient.hset('socketIds', userId, socket.id);
socket.on('message', async (data) => {
/**
* data {
* userId,
* message
* }
*/
const data2 = JSON.parse(data);
// get the socket.id based on the user ID
const socketId = await socketClient.hget('socketIds', data2.userId);
// send the message to the correct socket.id
io.to(socketId).emit('message', data.message);
};
});
So when I deploy this code to heroku, when started, it will create 3 connections to the same redis server. But what if 2-3-4-... people connect to this node.js-server? If 2 people connect, will there be 6 redis-connections, or only 3? Like: will the node.js-server initiate every time a users accesses the server 3 new redis connections, or will it always be 3 connections?
I'm trying to track all connections with CLIENT LIST in redis-cli, but I does not give me the correct thing I guess. I was just testing my code with only one user connection to the socket server and it gave me 1 client in redis (instead of 3 connections).
Thanks in advance.
It doesn't matter how many people are using the app, each client instance will have only 1 socket at any time, which means you'll see at most 3 clients per node process.
You see only 1 connection because by default ioredis initiates the connection when the first command is executed, and not when the client is created. You can call client.connect() in order to initiate the socket without executing a command.

Is it a good practice to close my connection after Sequelize operations?

currently I'm designing an app on top of Sequelize using NodeJS/TypeScript, and I'm wondering if it can cause performance issues not closing a connection.
For instance, in a micro service, I need data from 1 entity.
const resolver = async (,,{db}) => {
const entity1 = await db.models.Entity1.findOne()
return entity1
}
Is it required to close the connection after having called findOne?
My understanding is that the following config defines a number of concurrent connections and idle is a parameter making the connection manager closing the connection of idle ones:
module.exports = {
development: {
host: 'db.sqlite',
dialect: 'sqlite',
pool: {
max:5,
min:0,
idle:10000
}
},
test: {
host: 'test.sqlite',
dialect: 'sqlite',
pool: {
max:5,
min:0,
idle:10000
}
}
}
Any advice is welcome
Sequelize maintains an internal database connection pool, that's what the pool parameters are for, so this isn't necessary. Each call actually borrows a connection temporarily and then returns it to the pool when done.
Closing that connection manually may poison the pool and cause performance issues.
If you don't close the Sequelize connection, the micro-service will still run until the connection got timed out (idle time pool parameter).. I suggest to close Sequelize connection, at least in micro-services..

Websocket closing after 60 seconds of being idle while connection node server using AWS ELB

I have one node server running on EC2 instance and client is also running on same EC2 instance, Client open websocket connection to communicate node server, it is working in QA and Dev AWS environment but same web connection is getting close after 60 seconds of being idle in prod environment ,I am running client and node server behind ELB in aws environment.
Client Code:
ws = new WebSocket('ws://localhost:8443');
ws.onclose = function () {
console.log("Websocket connection has been closed.");
clientObj.emit('LogoffSuccess', 'LogoffSuccessfully');
};
ws.onerror=function(event)
{
console.log(event.data);
};
ws.addEventListener('open', function (event) {
console.log('Websocket connection has been opened');
ws.send(JSON.stringify(loginCreds));
});
Node server Code below:
const wss = new WebSocket.Server({ server: app });
const clients = {};
const idMap = {};
wss.on(`connection`, ws => {
const headers = ws.upgradeReq.headers;
const host = headers.host;
const key = ws.upgradeReq.headers[`sec-websocket-key`];
ctiServer.on(`responseMessage`, message => {
clients[message.AgentId].send(JSON.stringify(message));
});
ws.on(`message`, message => {
log.info(`Message received. Host: ${host}, Msg: ${message}`);
if (JSON.parse(message).EventName === `Login`) {
clients[JSON.parse(message).AgentId] = ws;
idMap[key] = JSON.parse(message).AgentId;
}
ctiServer.processIncomingRequest(message);
});
ws.on(`close`, () => {
log.info(`Connection closed. Host: ${host}`);
const message = {
EventName: `Logoff`,
AgentId: idMap[key],
EventData: {}
};
});
});
By default, Elastic Load Balancing sets the idle timeout value to 60 seconds. Therefore, if the target doesn't send some data at least every 60 seconds while the request is in flight, the load balancer can close the front-end connection. To ensure that lengthy operations such as file uploads have time to complete, send at least 1 byte of data before each idle timeout period elapses, and increase the length of the idle timeout period as needed.
https://docs.aws.amazon.com/elasticloadbalancing/latest/application/application-load-balancers.html#connection-idle-timeout
Note that your interests are best served by periodically sending traffic to keep the connection alive. You can set the idle timeout to up to 4000 seconds in an Application Load Balancer, but you will find that stateful intermediate network infrastructure (firewalls, NAT devices) tends to reset connections before they are actually idle for so long.
PING!
Write a ping implementation (or a nil message implementation)...
...otherwise the AWS proxy (probably nginx) will shut down the connection after a period of inactivity (60 seconds in your case, but it's a bit different on different systems).
Do you use NGINX? Their requests timeout after 60 seconds.
You can extended the timeout in the NGINX configuration file for your websockets specific location.
In your case it could look something like this when extending the timeout to an hour:
...
location / {
...
proxy_pass http://127.0.0.1:8443;
...
proxy_read_timeout 3600;
proxy_send_timeout 3600;
...
}
Also see this website for more information:
https://ubiq.co/tech-blog/increase-request-timeout-nginx/
https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_read_timeout
https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_send_timeout

How to do graceful stop for koajs server?

There are a lot of examples of graceful stop for expressjs, how can I achieve the same for koajs?
I would like to disconnect database connections as well
I have a mongoose database connection, and 2 oracle db connection (https://github.com/oracle/node-oracledb)
I created an npm package http-graceful-shutdown (https://github.com/sebhildebrandt/http-graceful-shutdown) some time ago. This works perfectly with http, express and koa. As you want to add also your own cleanup stuff, I modified the package, so that you now can add your own cleanup function, that will be called on shutdown. So basically this package handles all http shutdown things plus calling your cleanup function (if provided in the options):
const koa = require('koa');
const gracefulShutdown = require('http-graceful-shutdown');
const app = new koa();
...
server = app.listen(...); // app can be an express OR koa app
...
// your personal cleanup function - this one takes one second to complete
function cleanup() {
return new Promise((resolve) => {
console.log('... in cleanup')
setTimeout(function() {
console.log('... cleanup finished');
resolve();
}, 1000)
});
}
// this enables the graceful shutdown with advanced options
gracefulShutdown(server,
{
signals: 'SIGINT SIGTERM',
timeout: 30000,
development: false,
onShutdown: cleanup,
finally: function() {
console.log('Server gracefulls shutted down.....')
}
}
);
I have answered a variation of "how to terminate a HTTP server" many times on different node.js support channels. Unfortunately, I couldn't recommend any of the existing libraries because they are lacking in one or another way. I have since put together a package that (I believe) is handling all the cases expected of graceful HTTP server termination.
https://github.com/gajus/http-terminator
The main benefit of http-terminator is that:
it does not monkey-patch Node.js API
it immediately destroys all sockets without an attached HTTP request
it allows graceful timeout to sockets with ongoing HTTP requests
it properly handles HTTPS connections
it informs connections using keep-alive that server is shutting down by setting a connection: close header
it does not terminate the Node.js process
Usage with Koa:
import Koa from 'koa';
import {
createHttpTerminator,
} from 'http-terminator';
const app = new Koa();
const server = app.listen();
const httpTerminator = createHttpTerminator({
server,
});
await httpTerminator.terminate();
To make sure the Oracle DB connections are closed nicely, you can use a connection pool and call pool.close() with a drainTime of 0 or greater. This will let the app relatively cleanly interrupt any operation that is currently using a connection. It allows freeing the DB end of the connections without the DB waiting for whatever timeout period to expire before it cleans itself up. Even with two connections this is a solution I'd look at, since it doesn't matter that the pool is small. You may need to set the Oracle Net out-of-band break detection as well, see Connections and High Availability.
Modern versions of node have support for AbortController, so no need for external libraries. A Simple example:
const app = new Koa();
const server = http.createServer(app.callback());
const controller = new AbortController();
server.listen({
host: 'localhost',
port: 80,
signal: controller.signal
});
// middleware... etc.
app.use(async (ctx) => {
ctx.body = 'Hello World';
});
// Later, when you want to close the server.
controller.abort();

Resources