Jaki jest właściwy sposób używania modułu postgresql node.js?

95

Piszę aplikację node.js na Heroku i używam modułu pg . Nie mogę znaleźć „właściwego” sposobu uzyskania obiektu klienta dla każdego żądania, którego potrzebuję, aby wysłać zapytanie do bazy danych.

Dokumentacja wykorzystuje następujący kod:

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

Ale na pewno nie musisz wywoływać pg.connectwewnątrz każdej funkcji korzystającej z bazy danych, prawda? Widziałem inny kod , który to robi:

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

Skłaniam się ku drugiej opcji, ponieważ uważam, że bezpłatna instancja bazy danych dla Heroku jest i tak ograniczona do jednego połączenia, ale czy są jakieś wady zrobienia tego w ten sposób? Czy za każdym razem muszę sprawdzać, czy mój obiekt klienta jest nadal połączony?

Philip
źródło

Odpowiedzi:

158

Jestem autorem node-postgres . Po pierwsze, przepraszam, że dokumentacja nie wyjaśniła właściwej opcji: to moja wina. Postaram się to poprawić. Napisałem teraz streszczenie, aby to wyjaśnić, ponieważ rozmowa wydłużyła się zbyt długo dla Twittera.

Używanie pg.connectto sposób na poruszanie się w środowisku sieciowym.

Serwer PostgreSQL może obsłużyć tylko 1 zapytanie na raz na połączenie. Oznacza to, że jeśli masz 1 new pg.Client()połączenie globalne z backendem, cała aplikacja jest ograniczona w zależności od tego, jak szybko postgres może odpowiadać na zapytania. Dosłownie wyrówna wszystko, ustawiając w kolejce każde zapytanie. Tak, jest asynchroniczny, więc w porządku ... ale czy nie wolałbyś raczej pomnożyć przepustowości 10x? Użyj pg.connect set the pg.defaults.poolSizena coś rozsądnego (robimy 25-100, nie jesteśmy jeszcze pewni właściwej liczby).

new pg.Clientjest przeznaczony dla osób, które wiesz, co robisz. Gdy z jakiegoś powodu potrzebujesz jednego, długowiecznego klienta lub potrzebujesz bardzo uważnej kontroli cyklu życia. Dobrym tego przykładem jest użycie LISTEN/NOTIFY. Klient nasłuchujący musi być w pobliżu i być połączony i nie udostępniony, aby mógł prawidłowo obsługiwać NOTIFYwiadomości. Innym przykładem może być otwarcie klienta jednorazowego, aby zabić zawieszone rzeczy lub w skryptach wiersza poleceń.

Bardzo pomocną rzeczą jest scentralizowanie całego dostępu do bazy danych w aplikacji do jednego pliku. Nie zaśmiecaj pg.connecttelefonów ani nowych klientów przez cały czas. Miej taki plik, db.jsktóry wygląda mniej więcej tak:

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

W ten sposób możesz zmienić swoją implementację z pg.connectniestandardowej puli klientów lub cokolwiek innego i zmienić tylko rzeczy w jednym miejscu.

Przyjrzyj się modułowi node-pg-query , który właśnie to robi.

brianc
źródło
2
Przepraszam, jestem dość nowy w DBMS i nadal mam problem ze zrozumieniem tego, ale dlaczego nie chcemy zaśmiecać połączeń pg.connect? Czy to dla uproszczenia, czy ze względu na wydajność? Na przykład dzwonię do pg.connect raz dla każdej trasy, którą mam w mojej podstawowej aplikacji (wszystkie z tym samym ciągiem znaków). Czy to w porządku? Intuicyjnie wydaje się, że tworzy nowe połączenie z tą samą bazą danych za każdym razem, gdy ją wywołuję (czego nie chcę), ale czy wewnętrznie korzysta z puli połączeń? Dzięki.
user1164937
Niesamowite. Dlaczego stosujesz jedno połączenie na zapytanie zamiast jednego na żądanie? Szukałem odpowiedniego sposobu na udostępnienie połączenia w wielu zapytaniach w ramach żądania i rozważałem res.locals przed znalezieniem odpowiedzi tutaj.
Joe Lapp
2
Zaczekaj. Wygląda na to, że Twoje rozwiązanie tutaj nie obsługuje transakcji.
Joe Lapp
Powinno to być połączone z githubem.
Ryan Willis
1
Zauważ, że pg.connect został usunięty po v7 node-postgres aka pg. Zobacz stackoverflow.com/questions/45174120/pg-connect-not-a-function
Colin D
23

Jestem autorem pg-promise , które upraszcza korzystanie z node-postgres poprzez obietnice.

Porusza kwestie prawidłowego sposobu łączenia się z bazą danych i odłączania się od niej, wykorzystując pulę połączeń realizowaną przez node-postgres , m.in. zautomatyzowane transakcje.

Indywidualne żądanie w pg-promise sprowadza się do tego, co jest istotne dla Twojej logiki biznesowej:

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

tj. nie musisz zajmować się logiką połączeń podczas wykonywania zapytań, ponieważ konfigurujesz połączenie tylko raz, globalnie, na przykład:

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;

Więcej przykładów można znaleźć w samouczku „ Naucz się przez przykłady” lub na stronie głównej projektu .

Vitaly-t
źródło
Cześć, Heroku akceptuje tylko połączenia SSL. W pgtym jest określone przez pg.defaults.ssl = true;. Jak to robisz pg-promise?
ocram
@ocram github.com/vitaly-t/pg-promise/wiki /... lub możesz określić SSL w parametrach połączenia: github.com/vitaly-t/pg-promise/wiki/Connection-Syntax
vitaly-t
W większości z nich jestem nowy: javascript, obietnice, postgres itp. I właśnie tego potrzebowałem. Dziękuję Ci!!
Ryan Rodemoyer
1
@ocram Właśnie to działało, robiącpgp.pg.defaults.ssl = true;
CharlieC
czy stworzy to wiele połączeń, aby automatycznie poprawić przepustowość postgres, gdy wysyłamy wiele zapytań do postgres?
sundar
5

Basen jest teraz właściwą drogą. Coś takiego

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

może być używany jako db.query('<BEGIN,COMMIT,ROLLBACK,your query,anything')

amar
źródło
1

Lepiej jest utworzyć pulę pg globalnie i za każdym razem, gdy musisz wykonać operację db, użyj klienta, a następnie zwolnij go z powrotem do puli. Po zakończeniu wszystkich operacji dotyczących bazy danych zakończ pulę za pomocąpool.end()

Przykładowy kod -

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

        });
}

});  

Więcej szczegółów można znaleźć w moim poście na blogu - Źródło

Aniket Thakur
źródło
-1

Byłem zainteresowany bardzo prostym programem obsługi, więc stworzyłem własny, nie komplikując go zbytnio. Nie mam złudzeń, że jest super prosty, ale może pomóc niektórym zacząć. Zasadniczo łączy, uruchamia zapytania i obsługuje błędy.

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

Wtedy użyłbyś, nazywając to w ten sposób:

runQuery("SELECT * FROM table", function(result) {
  // Whatever you need to do with 'result'
}
JM-AGMS
źródło
To nawet nie zwalnia połączenia z powrotem do puli. Szybko wyczerpie basen. Podstawowy przykład na node-postgresstronie działa lepiej niż ten.
vitaly-t
-2

Oto jak to robię, coś w rodzaju „całego powyższego podejścia”

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()
Duane Fields
źródło
1
Więc skończysz bez zarządzania połączeniami, obsługi transakcji i obsługi zadań. Po co więc?
vitaly-t
1
co to za język? Kawa? berk
caub