web-dev-qa-db-ja.com

node.js postgresqlモジュールを使用する適切な方法は何ですか?

Herokuでnode.jsアプリを作成し、 pgモジュール を使用しています。データベースを照会する必要のある各要求に対してクライアントオブジェクトを取得する「正しい」方法がわかりません。

ドキュメントでは、次のようなコードを使用しています。

pg.connect(conString, function(err, client) {
  // Use the client to do things here
});

ただし、データベースを使用するすべての関数内でpg.connectを呼び出す必要はありません。 他のコード を見たことがあります:

var conString = process.env.DATABASE_URL || "tcp://postgres:[email protected]/postgres";
var client = new pg.Client(conString);
client.connect();
// client is a global so you can use it anywhere now

とにかく、Herokuの無料のデータベースインスタンスは1つの接続に限定されると考えているため、2番目のオプションに傾いていますが、この方法で行うことには欠点がありますか?クライアントオブジェクトを使用する前に毎回接続されているかどうかを確認する必要がありますか?

91
Philip

私は node-postgres の著者です。まず、ドキュメントが正しいオプションを明確にできなかったことをおaびします。それが私のせいです。私はそれを改善しようとします。 会話 がTwitterにとって長すぎたため、これを説明するために a Gist を書きました。

_pg.connect_を使用することは、Web環境で進む方法です。

PostgreSQLサーバーは、接続ごとに一度に1つのクエリしか処理できません。つまり、1つのグローバルnew pg.Client()がバックエンドに接続されている場合、postgresがクエリに応答する速度に基づいて、アプリ全体がボトルネックになります。文字通りすべてを並べ、各クエリをキューに入れます。ええ、それは非同期ですので、それは大丈夫です...しかし、あなたはむしろあなたのスループットを10倍に倍増しませんか? _pg.connect_を使用して、_pg.defaults.poolSize_を正気なものに設定します(25-100を実行しますが、正しい番号はまだわかりません)。

_new pg.Client_は、自分が何をしているかを知っている場合に使用します。何らかの理由で単一の長寿命クライアントが必要な場合、またはライフサイクルを非常に慎重に制御する必要がある場合。この良い例は、_LISTEN/NOTIFY_を使用する場合です。リスニングクライアントは、NOTIFYメッセージを適切に処理できるように、共有されずに接続されている必要があります。他の例は、ハングしたものを殺すために1回限りのクライアントを開くとき、またはコマンドラインスクリプトであるでしょう。

非常に役立つことの1つは、アプリ内のデータベースへのすべてのアクセスを1つのファイルに集中化することです。 _pg.connect_の呼び出しや新しいクライアントを散らかさないでください。次のような_db.js_のようなファイルがあります。

_module.exports = {
   query: function(text, values, cb) {
      pg.connect(function(err, client, done) {
        client.query(text, values, function(err, result) {
          done();
          cb(err, result);
        })
      });
   }
}
_

これにより、実装を_pg.connect_からクライアントのカスタムプールなどに変更することができ、1か所で変更するだけで済みます。

node-pg-queryモジュール を見てください。

152
brianc

私は pg-promise の作成者です。これにより、promiseによる node-postgres の使用が簡単になります。

node-postgres によって実装された接続プールを使用して、自動トランザクションなどのように、データベースへの適切な接続および切断方法に関する問題に対処します。

pg-promise の個々のリクエストは、ビジネスロジックに関連するものだけに要約されます。

db.any('SELECT * FROM users WHERE status = $1', ['active'])
    .then(data => {
        console.log('DATA:', data);
    })
    .catch(error => {
        console.log('ERROR:', error);
    });

つまり、次のようにグローバルに一度だけ接続を設定するため、クエリを実行するときに接続ロジックを扱う必要はありません。

const pgp = require('pg-promise')(/*options*/);

const cn = {
    Host: 'localhost', // server name or IP address;
    port: 5432,
    database: 'myDatabase',
    user: 'myUser',
    password: 'myPassword'
};
// alternative:
// const cn = 'postgres://username:[email protected]:port/database';

const db = pgp(cn); // database instance;

例で学ぶ チュートリアル、または プロジェクトのホームページ でさらに多くの例を見つけることができます。

22
vitaly-t

documentation からわかるように、両方のオプションが有効なので、どちらを選択してもかまいません。あなたのように、私は2番目の選択肢に行きます。

0
alessioalex

Pgプールをグローバルに作成し、db操作を行う必要があるたびにクライアントを使用してから、プールにリリースすることをお勧めします。すべてのdb操作が完了したら、pool.end()を使用してプールを終了します

サンプルコード-

let pool = new pg.Pool(dbConfig);
pool.connect(function(err, client, done) {

if (err) {
    console.error('Error connecting to pg server' + err.stack);
    callback(err);
} else {
    console.log('Connection established with pg db server');

    client.query("select * from employee", (err, res) => {

            if (err) {
                console.error('Error executing query on pg db' + err.stack);
                callback(err);
            } else {
                console.log('Got query results : ' + res.rows.length);


               async.each(res.rows, function(empRecord) {   
                        console.log(empRecord.name);
                });
            }
            client.release();

        });
}

});  

詳細については、私のブログ投稿- Source を参照してください。

0
Aniket Thakur