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


95

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:1234@localhost/postgres";
var client = new pg.Client(conString);
client.connect();
// client is a global so you can use it anywhere now

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

回答:


158

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

を使用するpg.connect Web環境に移動できます。

PostgreSQLサーバーは、接続ごとに一度に1つのクエリしか処理できません。つまりnew pg.Client()、バックエンドに1つのグローバルが接続されている場合、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モジュールを見てください。


2
申し訳ありませんが、DBMSを使用するのはかなり初心者ですが、これを理解するのにまだ問題がありますが、pg.connect呼び出しを「散らかしたくない」のはなぜですか。単純化のためですか、それともパフォーマンス上の理由によるものですか?たとえば、基本的なアプリで持っている各ルートでpg.connectを1回呼び出します(すべて同じconStringで)。よろしいですか?直感的には、呼び出したときに同じdbに新しい接続を作成しているように感じます(これは望ましくありません)が、プールされた接続を内部で使用していますか?ありがとう。
user1164937 2013年

驚くばかり。リクエストごとではなく、クエリごとに1つの接続を使用しているのはなぜですか?リクエスト内の複数のクエリ間で接続を共有する適切な方法を探しており、ここで答えを見つける前にres.localsを検討していました。
Joe Lapp、2014

2
あ、ちょっと待って。ここのソリューションはトランザクションをサポートしていないようです。
ジョーラップ2014

これはgithubにパーマリンクされているはずです。
ライアンウィリス

1
pg.connectはnode-postgres別名pgのv7以降で削除されたことに注意してください。stackoverflow.com/questions/45174120/pg-connect-not-a-functionを
コリンD

23

私は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:password@host:port/database';

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

Learn by Exampleチュートリアルまたはプロジェクトのホームページに、さらに多くの例があります


こんにちは。HerokuはSSL接続のみを受け入れます。ではpgこれで指定されますpg.defaults.ssl = true;。どのようにこれを行うのpg-promiseですか?
ocram


私はこれのほとんどに新しいです:javascript、promise、postgresなど。これがまさに私が必要とするものです。ありがとうございました!!
Ryan Rodemoyer 2016年

1
@ocram私はこれを実行することで機能しましたpgp.pg.defaults.ssl = true;
CharlieC 2017年

これにより、postgresに複数のクエリリクエストを送信したときに自動的にpostgresスループットを向上させる複数の接続が作成されますか?
サンダー

5

プールは今行く方法です。このようなもの

const { Pool } = require('pg');

    const pool = new Pool({
      connectionString: DATABASE_URL,
      ssl: false,
      max: 20,
      idleTimeoutMillis: 30000,
      connectionTimeoutMillis: 2000,
    });
    module.exports = {
        query: (text, params) => pool.query(text, params)
      }

それはとして使用することができます db.query('<BEGIN,COMMIT,ROLLBACK,your query,anything')


1

グローバルに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();

        });
}

});  

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


0

ドキュメントからわかるように、両方のオプションが有効であるため、どちらを選択してもかまいません。あなたとして、私は2番目の選択肢を選びます。


接続が切れた場合の再接続についてはどうですか?それは自動的に行われますか?エラー処理のwikiページは...空のgithub.com/brianc/node-postgres/wiki/Error-handling
alltom

私は別にそれを求めてきました:stackoverflow.com/questions/15619456/...
alltom

-1

私はこれの非常に単純なハンドラに興味があったので、複雑にしないで自分で作成しました。それが非常に基本的であるという幻想はありませんが、一部の人々が始めるのに役立つ可能性があります。基本的には、接続してクエリを実行し、エラーを処理します。

function runQuery(queryString, callback) {
  // connect to postgres database
  pg.connect(postgresDatabase.url,function(err,client,done) {
    // if error, stop here
    if (err) {console.error(err); done(); callback(); return;}
    // execute queryString
    client.query(queryString,function(err,result) {
      // if error, stop here
      if (err) {console.error(err+'\nQuery: '+queryString); done(); callback(); return;}
      // callback to close connection
      done();
      // callback with results
      callback(result.rows);
    });
  });
}

次に、次のように呼び出して使用します。

runQuery("SELECT * FROM table", function(result) {
  // Whatever you need to do with 'result'
}

これにより、プールへの接続が解放されることもありません。それはプールを本当に速く枯渇させます。node-postgresページの基本的な例はこれよりも優れています。
vitaly-t

-2

これが私のやり方です、「上記のすべてのアプローチ」のようなものです

Promise = require 'bluebird'
pg = module.exports = require 'pg'

Promise.promisifyAll pg.Client.prototype
Promise.promisifyAll pg.Client
Promise.promisifyAll pg.Connection.prototype
Promise.promisifyAll pg.Connection
Promise.promisifyAll pg.Query.prototype
Promise.promisifyAll pg.Query
Promise.promisifyAll pg

connectionString = process.env.DATABASE_URL

module.exports.queryAsync = (sql, values) ->
  pg.connectAsync connectionString
  .spread (connection, release) ->
    connection.queryAsync sql, values
    .then (result) ->
      console.log result.rows[0]
    .finally ->
      release()

1
その結果、接続管理、トランザクションサポート、タスクサポートがなくなります。では何がポイントなのでしょうか?
vitaly-t

1
何語ですか?コーヒー?バーク
caub 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.