когда отключать и когда заканчивать pg-клиент или пул

Мой стек - узел, экспресс и модуль pg. Я действительно пытаюсь понять документацию и некоторые устаревшие учебники. Я не знаю, когда и как отключить и завершить работу с клиентом.

Для некоторых маршрутов я решил использовать пул. Это мой код

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();
    })
});

На маршрутах CMS я использую клиент вместо пула, который имеет разные привилегии db, чем пул.

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);
      });
  })
}

В этом случае, если я получаю username already used и пытаюсь переписать его с другим именем пользователя, запрос getUser никогда не запустится, и страница зависает. Если я удалю client.end(); от обеих функций он будет работать.

Я смущен, поэтому, пожалуйста, совет о том, как и когда отключить и полностью закрыть пул или клиента. Любые подсказки, объяснения или руководства будут оценены.

Спасибо

Ответ 1

Во-первых, из документации pg *:

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
      })
  })

Этот код/​​конструкция является достаточным для того, чтобы заставить ваш пул работать, предоставляя вещи здесь. Если вы закроете приложение, соединение будет зависать нормально, так как пул создается хорошо, точно не повесить, даже если он обеспечивает ручной способ подвешивания, см. Последний раздел статьи. Также посмотрите на предыдущую красную секцию, в которой говорится: "Вы всегда должны возвращать клиента...", чтобы принять

  • обязательная client.release()
  • до принятия аргумента.
  • вы используете/закрываете клиент в своих обратных вызовах.

Затем из документации pg.client *:

Обычный текстовый запрос с обещанием

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())

мне кажется самым ясным синтаксисом:

  • вы завершаете клиента независимо от результатов.
  • вы получаете доступ к результату перед завершением работы с клиентом.
  • вы не открываете/закрываете клиента в своих обратных вызовах

Это такое сопоставление между двумя синтаксисами, которые могут сбивать с первого взгляда, но там нет волшебства, это синтаксис построения реализации. Сосредоточьтесь на своих обратных вызовах и запросах, а не на этих конструкциях, просто подберите самые элегантные для своих глаз и подайте их своим кодом.

* Я добавил комментарии//ваш xxx для ясности

Ответ 2

Вы не должны отключать пул при каждом запросе, предполагается, что пул соединений будет иметь "горячие" соединения.

Обычно у меня есть глобальное соединение при запуске, а соединение с пулом закрывается (если) прекращается; вам просто нужно выпустить соединение из пула каждый раз, когда запрос заканчивается, как вы это делали, и использовать тот же пул и в функции signup.

Иногда мне нужно сохранять соединения, я использую оболочку для функции запроса, которая проверяет, является ли соединение активным или нет, прежде чем выполнить запрос, но это просто оптимизация.

Если вы не хотите управлять открытыми/закрытыми соединениями/пулом или выпуском, вы можете попробовать https://github.com/vitaly-t/pg-promise, он будет управлять всем этим беззвучно, и он работает хорошо.

Ответ 3

Его довольно простое, клиент-соединение (одно соединение) открывается, запрос с ним, как только вы закончите, вы закончите его.

Концепция пула отличается, в случае с mysql: вы должны.release() подключиться обратно к пулу, как только вы закончите с ним, но похоже, что pg - это другая история:

Из проблемы в github repo: нельзя использовать пул после завершения вызова в пуле # 1635

"Невозможно использовать пул после завершения вызова в пуле"

Вы не можете повторно использовать пул после его закрытия (т.е. После вызова функции.end()). Вам нужно будет воссоздать пул и отказаться от старого.

Самый простой способ борьбы с объединением в Лямбда - вовсе не делать этого. Создайте свои собственные взаимодействия с базами данных и закройте их, когда они будут сделаны. Вы не можете поддерживать пул в циклах замораживания/оттаивания в любом случае, поскольку базовые TCP-сокеты будут закрыты.

Если открытие/закрытие соединений становится проблемой производительности, посмотрите на настройку внешнего пула, такого как pgbouncer.

Поэтому я бы сказал, что ваш лучший вариант - не заканчивать пул, если вы не закрываете сервер