Apa cara yang tepat untuk menggunakan modul node.js postgresql?


95

Saya menulis aplikasi node.js di Heroku dan menggunakan modul pg . Saya tidak dapat menemukan cara yang "benar" untuk mendapatkan objek klien untuk setiap permintaan yang saya perlukan untuk meminta database.

Dokumentasi menggunakan kode seperti ini:

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

Tapi tentunya Anda tidak perlu memanggil ke pg.connectdalam setiap fungsi yang menggunakan database bukan? Saya telah melihat kode lain yang melakukan ini:

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

Saya condong ke opsi kedua karena saya yakin instance database gratis untuk Heroku terbatas pada satu koneksi, tetapi apakah ada kekurangan untuk melakukannya dengan cara ini? Apakah saya perlu memeriksa apakah objek klien saya masih terhubung setiap saat sebelum saya menggunakannya?

Jawaban:


158

Saya adalah penulis node-postgres . Pertama, saya minta maaf karena dokumentasi gagal memperjelas opsi yang benar: itu salah saya. Saya akan mencoba memperbaikinya. Saya baru saja menulis Gist untuk menjelaskan hal ini karena percakapan menjadi terlalu panjang untuk Twitter.

Menggunakan pg.connectadalah cara untuk masuk ke lingkungan web.

Server PostgreSQL hanya dapat menangani 1 kueri pada satu waktu per koneksi. Itu berarti jika Anda memiliki 1 global yang new pg.Client()terhubung ke backend Anda, seluruh aplikasi Anda akan mengalami bottlekneck berdasarkan seberapa cepat postgres dapat menanggapi kueri. Ini benar-benar akan berbaris semuanya, mengantri setiap kueri. Ya, ini asinkron dan tidak apa-apa ... tetapi bukankah Anda lebih suka mengalikan throughput Anda dengan 10x? Gunakan pg.connect set the pg.defaults.poolSizeke sesuatu yang waras (kami lakukan 25-100, belum yakin nomor yang benar).

new pg.Clientadalah untuk saat Anda tahu apa yang Anda lakukan. Bila Anda membutuhkan satu klien berumur panjang karena alasan tertentu atau perlu mengontrol siklus hidup dengan sangat hati-hati. Contoh bagusnya adalah saat menggunakan LISTEN/NOTIFY. Klien mendengarkan harus ada dan terhubung dan tidak dibagikan sehingga dapat menangani NOTIFYpesan dengan benar . Contoh lain adalah ketika membuka klien 1-off untuk mematikan beberapa hal yang macet atau dalam skrip baris perintah.

Satu hal yang sangat membantu adalah memusatkan semua akses ke database Anda di aplikasi Anda ke satu file. Jangan membuang-buang pg.connecttelepon atau klien baru. Buat file seperti db.jsitu terlihat seperti ini:

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

Dengan cara ini Anda dapat mengubah implementasi Anda dari pg.connectmenjadi kumpulan klien khusus atau apa pun dan hanya perlu mengubah berbagai hal di satu tempat.

Lihat modul node-pg-query yang melakukan hal ini.


2
Maaf, saya baru mengenal DBMS dan saya masih kesulitan memahami hal ini, tetapi mengapa kita tidak ingin panggilan "litter pg.connect"? Apakah karena kesederhanaan atau karena alasan kinerja? Misalnya, saya memanggil pg.connect sekali di setiap rute yang saya miliki di aplikasi dasar saya (semua dengan konfigurasi yang sama). Apakah ini baik? Secara intuitif, rasanya seperti membuat koneksi baru ke db yang sama setiap kali saya memanggilnya (yang tidak saya inginkan), tetapi apakah itu menggunakan koneksi gabungan secara internal? Terima kasih.
pengguna1164937

Hebat. Mengapa Anda menggunakan satu koneksi per kueri, bukan satu per permintaan? Saya telah mencari cara yang tepat untuk berbagi koneksi di beberapa pertanyaan dalam satu permintaan dan telah mempertimbangkan res.locals sebelum menemukan jawaban Anda di sini.
Joe Lapp

2
Oh tunggu. Sepertinya solusi Anda di sini tidak mendukung transaksi.
Joe Lapp

Ini harus ditautkan secara permanen ke github.
Ryan Willis

1
Perhatikan bahwa pg.connect telah dihapus posting v7 dari node-postgres alias pg. Lihat stackoverflow.com/questions/45174120/pg-connect-not-a-function
Colin D

24

Saya adalah penulis pg-promise , yang menyederhanakan penggunaan node-postgres melalui promise .

Ini membahas masalah tentang cara yang benar untuk menghubungkan ke dan memutuskan dari database, menggunakan kumpulan koneksi yang diimplementasikan oleh node-postgres , antara lain, seperti transaksi otomatis.

Permintaan individu di pg-promise bermuara pada apa yang relevan dengan logika bisnis Anda:

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

yaitu Anda tidak perlu berurusan dengan logika koneksi saat menjalankan kueri, karena Anda mengatur koneksi hanya sekali, secara global, seperti ini:

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;

Anda dapat menemukan lebih banyak contoh di tutorial Belajar dengan Contoh , atau di halaman beranda proyek .


Hai, Heroku hanya menerima koneksi SSL. Dalam pghal ini ditentukan oleh pg.defaults.ssl = true;. Bagaimana Anda melakukan ini pg-promise?
ocram

@ocram github.com/vitaly-t/pg-promise/wiki/… , atau Anda dapat menentukan SSL dalam parameter koneksi: github.com/vitaly-t/pg-promise/wiki/Connection-Syntax
-t

Saya baru mengenal sebagian besar ini: javascript, promise, postgres, dll. Dan inilah yang saya butuhkan. Terima kasih!!
Ryan Rodemoyer

1
@ocram Saya baru saja mendapatkan ini bekerja dengan melakukanpgp.pg.defaults.ssl = true;
CharlieC

akankah ini membuat banyak koneksi untuk meningkatkan throughput postgres secara otomatis ketika kami memberikan banyak permintaan kueri ke postgres?
Minggu

5

Kolam renang adalah cara untuk pergi sekarang. Beberapa hal seperti ini

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

itu bisa digunakan sebagai db.query('<BEGIN,COMMIT,ROLLBACK,your query,anything')


1

Lebih baik membuat pg pool secara global dan setiap kali Anda perlu melakukan operasi db, gunakan klien dan kemudian lepaskan kembali ke pool. Setelah semua operasi db selesai, akhiri penggunaan poolpool.end()

Kode sampel -

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

        });
}

});  

Untuk lebih jelasnya, Anda dapat merujuk ke posting blog saya - Sumber


0

Seperti yang Anda lihat dari dokumentasi, kedua opsi tersebut valid, jadi pilih mana yang Anda inginkan. Seperti Anda, saya akan memilih pilihan kedua.


Bagaimana dengan menghubungkan kembali ketika koneksi terputus? Apakah itu dilakukan secara otomatis? Halaman wiki tentang penanganan kesalahan adalah ... kosong github.com/brianc/node-postgres/wiki/Error-handling
alltom

Saya telah menanyakannya secara terpisah: stackoverflow.com/questions/15619456/…
alltom

-1

Saya tertarik dengan handler yang sangat sederhana untuk ini jadi saya membuatnya sendiri tanpa membuatnya terlalu rumit. Saya tidak berangan-angan bahwa ini super dasar tetapi dapat membantu beberapa orang untuk memulai. Pada dasarnya, ini menghubungkan, menjalankan kueri dan menangani kesalahan untuk Anda.

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

Maka Anda akan menggunakan dengan menyebutnya seperti ini:

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

Ini bahkan tidak melepaskan koneksi kembali ke kolam. Ini akan menguras kolam dengan sangat cepat. Contoh dasar di node-postgreshalaman lebih baik dari ini.
vitaly-t

-2

Begini cara saya melakukannya, semacam "semua pendekatan di atas"

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
Jadi Anda akan berakhir tanpa manajemen koneksi, tidak ada dukungan transaksi dan tidak ada dukungan tugas. Lalu apa gunanya?
vitaly-t

1
itu bahasa apa? kopi? berk
caub
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.