web-dev-qa-db-ja.com

いつ切断し、pgクライアントまたはプールを終了するか

私のスタックは、node、express、および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();を削除すると、機能します。

混乱しているので、プールまたはクライアントを切断する方法とタイミング、および完全に終了する方法についてアドバイスしてください。ヒントや説明、チュートリアルを歓迎します。

ありがとうございました

23
slevin

まず、 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
      })
  })
_

このコード/構造は、十分な/プールを機能させるために作成され、ここにあなたのものを提供しますもの。アプリケーションをシャットダウンすると、プールは正常に作成されるため、接続が正常にハングします。プールが手動でハングする方法を提供している場合でも、ハングしないように、 記事 の最後のセクションを参照してください。また、前の赤いセクションで「常にクライアントを返品する必要があります...」を確認してください。

  • 必須のclient.release()命令
  • 引数にアクセスする前に。
  • コールバック内でクライアントをスコープ/閉鎖します。

Thenpg.client documentation *から:

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

私には最も明確な構文のようです:

  • 結果に関係なくクライアントを終了します。
  • クライアントにendingする前に結果にアクセスします。
  • コールバック内でクライアントをスコープ/クローズしない

一見混乱させるかもしれないのは、2つの構文間のこの種の対立ですが、そこには魔法がありません。それは実装構文です。 yourコールバックとクエリに注目します。これらの構成要素ではなく、目のために最もエレガントなものを選び、feedあなたのコードでそれを。

*わかりやすくするために、ここにコメント// xxxを追加しました

12
lucchi

すべてのクエリでプールを切断しないでください。接続プールは「ホット」接続を持つために使用されることになっています。

私は通常、起動時にグローバル接続を使用し、アプリケーションの停止時にプール接続を閉じます。既に行っているように、クエリが終了するたびにプールから接続を解放し、signup関数でも同じプールを使用する必要があります。

接続を保存する必要がある場合があります。クエリを実行する前に接続がアクティブかどうかを確認するクエリ関数のラッパーを使用しますが、これは単なる最適化です。

オープン/クローズ接続/プールまたはリリースを管理したくない場合は、 https://github.com/vitaly-t/pg-promise を試してみてください。それはすべてを静かに管理しますそしてそれはうまく機能します。

7

その非常に単純な、クライアント接続(単一の接続)が開き、それを照会すると、終了したら終了します。

mysqlの場合、プールの概念は異なります。いったん終了したら、接続を.release()してプールに戻す必要がありますが、pgの場合は別の話:

githubリポジトリの問題から:プールでendを呼び出した後にプールを使用できません#1635

"プールでendを呼び出した後にプールを使用できません"

プールを閉じた後(つまり、.end()関数を呼び出した後)、プールを再利用することはできません。プールを再作成して、古いプールを破棄する必要があります。

ラムダでプーリングを処理する最も簡単な方法は、プーリングをまったく行わないことです。 データベースインタラクションが独自の接続を作成し、完了したらそれらを閉じるようにします。基礎となるTCP=ソケットは閉じられます。

接続のオープン/クローズがパフォーマンスの問題になる場合は、pgbouncerのような外部プールのセットアップを検討してください。

したがって、サーバーをシャットダウンしない限り、プールを終了しないのが最良のオプションであると言えます

0
EMX