Was ist der richtige Weg, um das postgresql-Modul von node.js zu verwenden?


95

Ich schreibe eine node.js App auf Heroku und benutze das pg Modul . Ich kann nicht herausfinden, wie ich für jede Anforderung, die ich zum Abfragen der Datenbank benötige, ein Client-Objekt erhalten kann.

In der Dokumentation wird folgender Code verwendet:

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

Aber Sie müssen doch nicht pg.connectjede Funktion aufrufen , die die Datenbank verwendet, oder? Ich habe anderen Code gesehen , der dies tut:

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

Ich neige zur zweiten Option, da ich glaube, dass die kostenlose Datenbankinstanz für Heroku sowieso auf eine Verbindung beschränkt ist, aber gibt es irgendwelche Nachteile, wenn ich das so mache? Muss ich jedes Mal überprüfen, ob mein Client-Objekt noch verbunden ist, bevor ich es verwende?

Antworten:


158

Ich bin der Autor von Node-Postgres . Zunächst entschuldige ich mich, dass die Dokumentation nicht die richtige Option klargestellt hat: Das ist meine Schuld. Ich werde versuchen, es zu verbessern. Ich habe gerade einen Gist geschrieben , um dies zu erklären, weil das Gespräch für Twitter zu lang wurde.

Verwenden pg.connectist der Weg in eine Webumgebung.

Der PostgreSQL-Server kann jeweils nur eine Abfrage pro Verbindung verarbeiten. Das heißt, wenn Sie 1 Global new pg.Client()mit Ihrem Backend verbunden haben, wird Ihre gesamte App mit einem Flaschenhals versehen, basierend darauf, wie schnell Postgres auf Anfragen antworten können. Es wird buchstäblich alles in einer Reihe stehen und jede Abfrage in die Warteschlange stellen. Ja, es ist asynchron und das ist in Ordnung ... aber würden Sie Ihren Durchsatz nicht lieber mit dem 10-fachen multiplizieren? Verwenden Sie pg.connect set the pg.defaults.poolSize auf etwas Vernünftiges (wir machen 25-100, noch nicht sicher, welche Nummer richtig ist).

new pg.Clientist für, wenn Sie wissen, was Sie tun. Wenn Sie aus irgendeinem Grund einen einzigen langlebigen Kunden benötigen oder den Lebenszyklus sehr sorgfältig steuern müssen. Ein gutes Beispiel hierfür ist die Verwendung LISTEN/NOTIFY. Der abhörende Client muss vorhanden und verbunden sein und darf nicht gemeinsam genutzt werden, damit er NOTIFYNachrichten ordnungsgemäß verarbeiten kann. Ein anderes Beispiel wäre das Öffnen eines einmaligen Clients zum Beenden von hängengebliebenen Dingen oder in Befehlszeilenskripten.

Eine sehr hilfreiche Sache ist es, den gesamten Zugriff auf Ihre Datenbank in Ihrer App in einer Datei zu zentralisieren. Werfen Sie keine pg.connectAnrufe oder neuen Kunden. Eine solche Datei db.jssieht ungefähr so ​​aus:

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

Auf diese Weise können Sie Ihre Implementierung von pg.connecteinem benutzerdefinierten Pool von Clients oder was auch immer ändern und müssen die Dinge nur an einem Ort ändern.

Schauen Sie sich das node-pg-query-Modul an , das genau dies tut.


2
Entschuldigung, ich bin ziemlich neu in DBMS und habe immer noch ein Problem damit, dies zu verstehen. Aber warum wollen wir keine pg.connect-Aufrufe "wegwerfen"? Ist es der Einfachheit halber oder aus Leistungsgründen? Zum Beispiel rufe ich pg.connect einmal in jeder der Routen auf, die ich in meiner Basis-App habe (alle mit der gleichen Steuerung). Ist das okay? Intuitiv fühlt es sich so an, als würde bei jedem Aufruf eine neue Verbindung zu derselben Datenbank hergestellt (was ich nicht möchte), aber werden die gepoolten Verbindungen intern verwendet? Vielen Dank.
user1164937

Genial. Warum verwenden Sie eine Verbindung pro Abfrage anstelle einer pro Anfrage? Ich habe nach einer geeigneten Möglichkeit gesucht, eine Verbindung über mehrere Abfragen innerhalb einer Anfrage hinweg zu teilen, und habe res.locals in Betracht gezogen, bevor ich hier Ihre Antwort gefunden habe.
Joe Lapp

2
Oh, Moment mal. Es sieht so aus, als würde Ihre Lösung hier keine Transaktionen unterstützen.
Joe Lapp

Dies sollte mit dem Github verbunden sein.
Ryan Willis

1
Beachten Sie, dass pg.connect nach v7 von node-postgres aka pg entfernt wurde. Siehe stackoverflow.com/questions/45174120/pg-connect-not-a-function
Colin D

23

Ich bin der Autor von pg-versprechen , das die Verwendung von Node-Postgres über Versprechen vereinfacht .

Es befasst sich mit den Problemen hinsichtlich der richtigen Art und Weise, wie eine Verbindung zur Datenbank hergestellt und von dieser getrennt werden kann. Dabei wird unter anderem der von Node-Postgres implementierte Verbindungspool verwendet , beispielsweise automatisierte Transaktionen.

Eine individuelle Anfrage in pg-versprechen läuft darauf hinaus, was für Ihre Geschäftslogik relevant ist:

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

Das heißt, Sie müssen sich beim Ausführen von Abfragen nicht mit der Verbindungslogik befassen, da Sie die Verbindung nur einmal global wie folgt einrichten:

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;

Weitere Beispiele finden Sie im Tutorial Lernen durch Beispiel oder auf der Homepage des Projekts .


Hallo, Heroku akzeptiert nur SSL-Verbindungen. In pgdiesem wird angegeben durch pg.defaults.ssl = true;. Wie machst du das in pg-promise?
Ocram

@ocram github.com/vitaly-t/pg-promise/wiki/… , oder Sie können SSL in den Verbindungsparametern angeben: github.com/vitaly-t/pg-promise/wiki/Connection-Syntax
vitaly-t

Ich bin neu in den meisten Dingen: Javascript, Versprechen, Postgres usw. und genau das brauchte ich. Danke dir!!
Ryan Rodemoyer

1
@ocram Ich habe gerade das zum Laufen gebrachtpgp.pg.defaults.ssl = true;
CharlieC

Werden dadurch mehrere Verbindungen hergestellt, um den Postgres-Durchsatz automatisch zu verbessern, wenn wir Postgres mehrere Abfrageanforderungen stellen?
Sundar

5

Pool ist der richtige Weg. So etwas

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

es kann verwendet werden als db.query('<BEGIN,COMMIT,ROLLBACK,your query,anything')


1

Es ist besser, einen pg-Pool global zu erstellen. Verwenden Sie den Client jedes Mal, wenn Sie eine Datenbankoperation ausführen müssen, und geben Sie ihn dann wieder für den Pool frei. Sobald alle Datenbankoperationen abgeschlossen sind, beenden Sie den Pool mitpool.end()

Beispielcode -

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

        });
}

});  

Weitere Informationen finden Sie in meinem Blog-Beitrag - Quelle



-1

Ich war an einem sehr einfachen Handler interessiert, also habe ich meinen eigenen gemacht, ohne ihn zu kompliziert zu machen. Ich mache mir keine Illusionen darüber, dass es super einfach ist, aber es könnte einigen Leuten helfen, anzufangen. Grundsätzlich stellt es eine Verbindung her, führt Abfragen aus und behandelt Fehler für Sie.

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

Dann würden Sie es verwenden, indem Sie es so nennen:

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

Dadurch wird nicht einmal die Verbindung zum Pool freigegeben. Es wird den Pool sehr schnell erschöpfen. Das grundlegende Beispiel auf node-postgresSeite macht es besser.
Vitaly-t

-2

Hier ist, wie ich es mache, eine Art "all der oben genannten Ansatz"

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
Sie haben also kein Verbindungsmanagement, keine Transaktionsunterstützung und keine Aufgabenunterstützung. Was ist der Sinn dann?
Vitaly-t

1
welche Sprache ist das? Kaffee? berk
caub
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.