Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

when to disconnect and when to end a pg client or pool

My stack is node, express and the pg module. I really try to understand by the documentation and some outdated tutorials. I dont know when and how to disconnect and to end a client.

For some routes I decided to use a pool. This is my code

const pool = new pg.Pool({
  user: 'pooluser',host: 'localhost',database: 'mydb',password: 'pooluser',port: 5432});

pool.on('error', (err, client) => {
  console.log('error ', err);  process.exit(-1);
});

app.get('/', (req, res)=>{
  pool.connect()
    .then(client => {
      return client.query('select ....')
            .then(resolved => {
              client.release();
              console.log(resolved.rows);
            })
            .catch(e => { 
              client.release();
              console.log('error', e);
            })
      pool.end();
    })
});

In the routes of the CMS, I use client instead of pool that has different db privileges than the pool.

const client = new pg.Client({
  user: 'clientuser',host: 'localhost',database: 'mydb',password: 'clientuser',port: 5432});    
client.connect();

const signup = (user) => {
  return new Promise((resolved, rejeted)=>{
    getUser(user.email)
    .then(getUserRes => {
      if (!getUserRes) {
        return resolved(false);
      }            
            client.query('insert into user(username, password) values ($1,$2)',[user.username,user.password])
              .then(queryRes => {
                client.end();
                resolved(true);
              })
              .catch(queryError => {
                client.end();
                rejeted('username already used');
              });
    })
    .catch(getUserError => {
      return rejeted('error');
    });
  }) 
};

const getUser = (username) => {
  return new Promise((resolved, rejeted)=>{
    client.query('select username from user WHERE username= $1',[username])
      .then(res => {
        client.end();
        if (res.rows.length == 0) {
          return resolved(true);
        }
        resolved(false);
      })
      .catch(e => {
        client.end();
        console.error('error ', e);
      });
  })
}

In this case if I get a username already used and try to re-post with another username, the query of the getUser never starts and the page hangs. If I remove the client.end(); from both functions, it will work.

I am confused, so please advice on how and when to disconnect and to completely end a pool or a client. Any hint or explanation or tutorial will be appreciated.

Thank you

like image 558
slevin Avatar asked May 23 '18 21:05

slevin


People also ask

What is difference between pool and client Postgres?

You have two options that you can connect to a PostgreSQL server with the node-postgres module. One of the options is to use a single client. The other method is to use a connection pool. However, if your application is using the database very frequently, the pool will be a better option than using a single client.

What is a PG pool?

Pgpool-II is a proxy software that sits between PostgreSQL servers and a PostgreSQL database client. It provides the following features: Connection Pooling.


3 Answers

First, from the pg documentation*:

const { Pool } = require('pg')

const pool = new Pool()

// the pool with emit an error on behalf of any idle clients
// it contains if a backend error or network partition happens
pool.on('error', (err, client) => {
  console.error('Unexpected error on idle client', err) // your callback here
  process.exit(-1)
})

// promise - checkout a client
pool.connect()
  .then(client => {
    return client.query('SELECT * FROM users WHERE id = $1', [1]) // your query string here
      .then(res => {
        client.release()
        console.log(res.rows[0]) // your callback here
      })
      .catch(e => {
        client.release()
        console.log(err.stack) // your callback here
      })
  })

This code/construct is suficient/made to get your pool working, providing the your thing here things. If you shut down your application, the connection will hang normaly, since the pool is created well, exactly not to hang, even if it does provides a manual way of hanging, see last section of the article. Also look at the previous red section which says "You must always return the client..." to accept

  • the mandatory client.release() instruction
  • before accesing argument.
  • you scope/closure client within your callbacks.

Then, from the pg.client documentation*:

Plain text query with a promise

const { Client } = require('pg').Client
const client = new Client()
client.connect()
client.query('SELECT NOW()') // your query string here
  .then(result => console.log(result)) // your callback here
  .catch(e => console.error(e.stack)) // your callback here
  .then(() => client.end())

seems to me the clearest syntax:

  • you end the client whatever the results.
  • you access the result before ending the client.
  • you don´t scope/closure the client within your callbacks

It is this sort of oposition between the two syntaxes that may be confusing at first sight, but there is no magic in there, it is implementation construction syntax. Focus on your callbacks and queries, not on those constructs, just pick up the most elegant for your eyes and feed it with your code.

*I added the comments // your xxx here for clarity

like image 197
lucchi Avatar answered Oct 19 '22 21:10

lucchi


You shouldn't disconnect the pool on every query, connection pool is supposed to be used to have "hot" connections.

I usually have a global connection on startup and the pool connection close on (if) application stop; you just have to release the connection from pool every time the query ends, as you already do, and use the same pool also in the signup function.

Sometimes I need to preserve connections, I use a wrapper to the query function that checks if the connection is active or not before perform the query, but it's just an optimization.

In case you don't want to manage open/close connections/pool or release, you could try https://github.com/vitaly-t/pg-promise, it manage all that stuff silently and it works well.

like image 43
Simone Sanfratello Avatar answered Oct 19 '22 20:10

Simone Sanfratello


The documentation over node-postgres's github says:

pro tip: unless you need to run a transaction (which requires a single client for multiple queries) or you have some other edge case like streaming rows or using a cursor you should almost always just use pool.query. Its easy, it does the right thing ™️, and wont ever forget to return clients back to the pool after the query is done.

So for non-transactional query, calling below code is enough.

var pool = new Pool()

pool.query('select username from user WHERE username= $1',[username], function(err, res) {

  console.log(res.rows[0].username)

})

By using pool.query, the library will take care of releasing the client after the query is done.

like image 9
milhamj Avatar answered Oct 19 '22 21:10

milhamj