Qual è il modo corretto di utilizzare il modulo postgresql node.js?


95

Sto scrivendo un'app node.js su Heroku e utilizzo il modulo pg . Non riesco a capire il modo "giusto" per ottenere un oggetto client per ogni richiesta di cui ho bisogno per interrogare il database.

La documentazione utilizza codice come questo:

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

Ma sicuramente non è necessario chiamare pg.connectall'interno di ogni funzione che utilizza il database, giusto? Ho visto altro codice che fa questo:

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

Sono propenso alla seconda opzione poiché credo che l'istanza di database gratuita per Heroku sia comunque limitata a una connessione, ma ci sono degli svantaggi nel farlo in questo modo? Devo controllare se il mio oggetto client è ancora connesso ogni volta prima di utilizzarlo?

Risposte:


158

Sono l'autore di node-postgres . Innanzitutto, mi scuso per il fatto che la documentazione non è riuscita a rendere chiara l'opzione giusta: è colpa mia. Proverò a migliorarlo. Ho scritto un Gist proprio ora per spiegare questo perché la conversazione è diventata troppo lunga per Twitter.

L'utilizzo pg.connectè la strada da percorrere in un ambiente web.

Il server PostgreSQL può gestire solo 1 query alla volta per connessione. Ciò significa che se hai 1 globale new pg.Client()connesso al tuo back-end, l'intera app è colma di bottiglia in base alla velocità con cui postgres può rispondere alle domande. Letteralmente allineerà tutto, accodando ogni query. Sì, è asincrono e quindi va bene ... ma non preferiresti moltiplicare il tuo throughput per 10 volte? Usa pg.connect set the pg.defaults.poolSizesu qualcosa di sano (facciamo 25-100, non siamo ancora sicuri del numero giusto).

new pg.Clientè per quando sai cosa stai facendo. Quando hai bisogno di un singolo cliente di lunga durata per qualche motivo o devi controllare molto attentamente il ciclo di vita. Un buon esempio di ciò è quando si utilizza LISTEN/NOTIFY. Il client in ascolto deve essere presente, connesso e non condiviso in modo da poter gestire correttamente i NOTIFYmessaggi. Un altro esempio potrebbe essere l'apertura di un client singolo per eliminare alcune cose sospese o negli script della riga di comando.

Una cosa molto utile è centralizzare tutti gli accessi al tuo database nella tua app in un unico file. Non sporcare pg.connectchiamate o nuovi clienti dappertutto. Avere un file del genere db.jsassomiglia a questo:

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

In questo modo puoi cambiare la tua implementazione da pg.connecta un pool personalizzato di client o qualsiasi altra cosa e devi solo cambiare le cose in un posto.

Dai un'occhiata al modulo node-pg-query che fa proprio questo.


2
Scusate, sono abbastanza nuovo nel DBMS e ho ancora problemi a capirlo, ma perché non vogliamo "sparpagliare pg.connect" chiamate? È per semplicità o per motivi di prestazioni? Ad esempio, chiamo pg.connect una volta in ciascuno dei percorsi che ho nella mia app di base (tutti con la stessa conString). Va bene? Intuitivamente, sembra che stia effettuando una nuova connessione allo stesso db ogni volta che lo chiamo (cosa che non voglio), ma utilizza internamente le connessioni in pool? Grazie.
user1164937

Eccezionale. Perché utilizzi una connessione per query invece di una per richiesta? Stavo cercando un modo appropriato per condividere una connessione tra più query all'interno di una richiesta e stavo prendendo in considerazione res.locals prima di trovare la tua risposta qui.
Joe Lapp

2
Oh aspetta. Sembra che la tua soluzione qui non supporti le transazioni.
Joe Lapp

Questo dovrebbe essere collegato a GitHub.
Ryan Willis

1
Nota che pg.connect è stato rimosso dopo la v7 di node-postgres aka pg. Vedi stackoverflow.com/questions/45174120/pg-connect-not-a-function
Colin D

24

Sono l'autore di pg-promise , che semplifica l'uso di node-postgres tramite promises.

Affronta i problemi sul modo giusto di connettersi e disconnettersi dal database, utilizzando il pool di connessioni implementato da node-postgres , tra le altre cose, come le transazioni automatizzate.

Una richiesta individuale in pg-promise si riduce a ciò che è rilevante per la tua logica aziendale:

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

cioè non è necessario occuparsi della logica di connessione durante l'esecuzione delle query, perché si configura la connessione solo una volta, a livello globale, in questo modo:

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;

Puoi trovare molti altri esempi nel tutorial Impara con l'esempio o nella home page del progetto .


Ciao, Heroku accetta solo connessioni SSL. In pgquesto è specificato da pg.defaults.ssl = true;. Come fai a farlo pg-promise?
ocram

@ocram github.com/vitaly-t/pg-promise/wiki/… , oppure puoi specificare SSL all'interno dei parametri di connessione: github.com/vitaly-t/pg-promise/wiki/Connection-Syntax
vitaly-t

Sono nuovo alla maggior parte di questo: javascript, promesse, postgres, ecc. E questo è esattamente ciò di cui avevo bisogno. Grazie!!
Ryan Rodemoyer

1
@ocram ho appena ottenuto questo lavoro facendopgp.pg.defaults.ssl = true;
CharlieC

questo creerà più connessioni per migliorare automaticamente il throughput di postgres quando forniamo più richieste di query a postgres?
domenica

5

La piscina è la strada da percorrere adesso. Qualcosa del genere

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

può essere usato come db.query('<BEGIN,COMMIT,ROLLBACK,your query,anything')


1

È meglio creare un pool pg a livello globale e ogni volta che è necessario eseguire un'operazione db utilizzare il client e quindi rilasciarlo nuovamente nel pool. Una volta completate tutte le operazioni di database, terminare il pool utilizzandopool.end()

Codice di esempio -

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

        });
}

});  

Per maggiori dettagli, puoi fare riferimento al mio post sul blog - Fonte



-1

Ero interessato a un gestore molto semplice per questo, quindi ho creato il mio senza complicarlo. Non mi illudo che sia super semplice, ma potrebbe aiutare alcune persone a iniziare. Fondamentalmente, si connette, esegue query e gestisce gli errori per te.

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

Quindi lo useresti chiamandolo in questo modo:

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

Questo non rilascia nemmeno la connessione al pool. Si esaurirà rapidamente la piscina. L'esempio di base sulla node-postgrespagina fa meglio di questo.
vitaly-t

-2

Ecco come lo faccio, una sorta di "tutto l'approccio di cui sopra"

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
Quindi ti ritroverai senza gestione della connessione, nessun supporto per le transazioni e nessun supporto per le attività. Qual è il punto allora?
vitaly-t

1
che lingua è? caffè? berk
caub
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.