2011-12-13 3 views
75

Я пишу приложение node.js на Heroku и используя pg module. Я не могу понять, «правильный» способ получить клиентский объект для каждого запроса, который мне нужен для запроса базы данных.Каков правильный способ использования модуля node.js postgresql?

документация использует такой код:

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

Но, конечно, вам не нужно вызывать pg.connect внутри каждую функцию, которая использует право базы данных? Я видел other code, что делает это:

var conString = process.env.DATABASE_URL || "tcp://postgres:[email protected]/postgres"; 
var client = new pg.Client(conString); 
client.connect(); 
// client is a global so you can use it anywhere now 

Я склоняюсь к второму варианту, так как я считаю, что свободный экземпляр базы данных для Heroku ограничивается одним соединением в любом случае, но есть какие-либо недостатки делать это таким образом? Нужно ли проверять, все ли мой клиентский объект все еще подключен каждый раз, прежде чем использовать его?

ответ

131

Я являюсь автором node-postgres. Во-первых, я приношу свои извинения, что документация не позволила сделать правильный выбор: это моя ошибка. Я постараюсь улучшить его. Я написал a Gist только сейчас, чтобы объяснить это, потому что the conversation слишком долго вырос для Twitter.

Использование pg.connect является путь в веб-среде.

Сервер PostgreSQL может обрабатывать только один запрос за один раз за соединение. Это означает, что если у вас есть 1 глобальный new pg.Client(), подключенный к вашему бэкэнд, все ваше приложение буксируется в зависимости от того, насколько быстро postgres может отвечать на запросы. Это буквально выровнят все, очереди каждого запроса. Да, это асинхронно, и так все в порядке ... но не могли бы вы умножить свою пропускную способность на 10 раз? Используйте pg.connect, установите pg.defaults.poolSize на что-то нормальное (мы делаем 25-100, не уверены правый номер еще).

new pg.Client для тех, кто знает, что вы делаете. Когда вам нужен один долговечный клиент по какой-либо причине или нужно очень тщательно контролировать жизненный цикл. Хорошим примером этого является использование LISTEN/NOTIFY. Слушающий клиент должен быть рядом, а подключен и не используется, поэтому он может правильно обрабатывать сообщения NOTIFY. Другим примером может быть открытие 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 в пользовательский пул клиентов или независимо от того, и только должны изменить положение вещей в одном месте.

Посмотрите на node-pg-query module, который делает именно это.

+2

Извините, я довольно новичок в СУБД, и у меня все еще есть проблема с пониманием этого, но почему мы не хотим «мусор pg.connect», звонки? Это для простоты или из-за соображений производительности? Например, я вызываю pg.connect один раз в каждом из маршрутов, которые у меня есть в моем базовом приложении (все с тем же conString). Это нормально? Интуитивно, кажется, что он создает новое соединение с тем же самым db, когда я его вызываю (чего я не хочу), но использует ли он объединенные соединения внутри? Благодарю. – user1164937

+0

Awesome. Почему вы используете одно соединение для каждого запроса вместо одного запроса? Я искал подходящий способ совместного использования соединения по нескольким запросам в запросе и рассматривал res.locals, прежде чем найти свой ответ здесь. –

+2

О, подождите. Похоже, ваше решение здесь не поддерживает транзакции. –

0

Как вы можете видеть из documentation, оба варианта действительны, поэтому выбирайте в зависимости от того, что вы предпочитаете. Как вы, я бы пошел со вторым выбором.

+0

Что о повторном подключении, когда соединение падает? Это делается автоматически? Страница wiki при обработке ошибок ... пустой https://github.com/brianc/node-postgres/wiki/Error-handling – alltom

+0

Я попросил его отдельно: http://stackoverflow.com/questions/15619456/ how-do-i-use-node-postgres-in-a-server – alltom

19

Я являюсь автором pg-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:[email protected]:port/database'; 

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

Вы можете найти много примеров в Learn by Example учебнике или на project's home page.

+0

Привет, Heroku принимает только SSL-соединения. В 'pg' это указано' pg.defaults.ssl = true; '. Как вы это делаете в 'pg-prom'? – ocram

+0

@ocram https://github.com/vitaly-t/pg-promise/wiki/FAQ#how-to-access-the-instance-of-node-postgres-thats-used или вы можете указать SSL в пределах параметры подключения: https://github.com/vitaly-t/pg-promise/wiki/Connection-Syntax –

+0

Я новичок в большинстве из этого: javascript, обещания, postgres и т. д., и это именно то, что мне нужно. Спасибо!! –

-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

Таким образом, вы не получите никакого управления соединением, без поддержки транзакций и без поддержки задач. Тогда какой смысл? –

+1

какой язык? кофе? berk – caub

-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' 
} 
+0

Это даже не выводит соединение обратно в пул. Он будет быстро истощать бассейн. Основной пример на странице «node-postgres» делает это лучше. –

0

Лучше создать пул пг глобально и каждый раз, когда вам нужно сделать операцию дб использовать клиент, а затем отпустить его обратно в бассейн. После того, как все дБ операции выполняются в конечном бассейне с помощью 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(); 

     }); 
} 

}); 

Source

Смежные вопросы