Fix native constructor and pool exports (#1061)

This commit is contained in:
Brian C 2016-06-24 00:52:28 -05:00 committed by GitHub
parent e2830ac847
commit 812277f99f
7 changed files with 121 additions and 55 deletions

126
README.md
View File

@ -3,7 +3,7 @@
[![Build Status](https://secure.travis-ci.org/brianc/node-postgres.svg?branch=master)](http://travis-ci.org/brianc/node-postgres) [![Build Status](https://secure.travis-ci.org/brianc/node-postgres.svg?branch=master)](http://travis-ci.org/brianc/node-postgres)
[![Dependency Status](https://david-dm.org/brianc/node-postgres.svg)](https://david-dm.org/brianc/node-postgres) [![Dependency Status](https://david-dm.org/brianc/node-postgres.svg)](https://david-dm.org/brianc/node-postgres)
PostgreSQL client for node.js. Pure JavaScript and optional native libpq bindings. Non-blocking PostgreSQL client for node.js. Pure JavaScript and optional native libpq bindings.
## Install ## Install
@ -11,28 +11,69 @@ PostgreSQL client for node.js. Pure JavaScript and optional native libpq bindin
$ npm install pg $ npm install pg
``` ```
## Intro & Examples
## Examples ### Simple example
```js
var pg = require('pg');
// instantiate a new client
// the client will read connection information from
// the same environment varaibles used by postgres cli tools
var client = new pg.Client();
// connect to our PostgreSQL server instance
client.connect(function (err) {
if (err) throw err;
// execute a query on our database
client.query('SELECT $1::text as name', ['brianc'], function (err, result) {
if (err) throw err;
// just print the result to the console
console.log(result.rows[0]); // outputs: { name: 'brianc' }
// disconnect the client
client.end(function (err) {
if (err) throw err;
});
});
});
```
### Client pooling ### Client pooling
Generally you will access the PostgreSQL server through a pool of clients. A client takes a non-trivial amount of time to establish a new connection. A client also consumes a non-trivial amount of resources on the PostgreSQL server - not something you want to do on every http request. Good news: node-postgres ships with built in client pooling. If you're working on something like a web application which makes frequent queries you'll want to access the PostgreSQL server through a pool of clients. Why? There is ~20-30 millisecond delay (YMMV) when connecting a new client to the PostgreSQL server because of the startup handshake. Also, PostgreSQL can only support a limited number of clients...it depends on the amount of ram on your database server, but generally more than 100 clients at a time is a bad thing. :tm: Finally
PostgreSQL can only execute 1 query at a time per connected client.
With that in mind we can imagine a situtation where you have a web server which connects and disconnects a new client for every web request or every query (don't do this!). If you get only 1 request at a time everything will seem to work fine, though it will be a touch slower due to the connection overhead. Once you get >500 simultaneous requests your web server will attempt to open 500 connections to the PostgreSQL backend and :boom: you'll run out of memory on the PostgreSQL server, it will become
unresponsive, your app will seem to hang, and everything will break. Boooo!
__Good news__: node-postgres ships with built in client pooling.
```javascript ```javascript
var Pool = require('pg').Pool; var pg = require('pg');
// create a config to configure both pooling behavior
// and client options
// note: all config is optional and the environment variables
// will be read if the config is not present
var config = { var config = {
user: 'foo', user: 'foo', //env var: PGUSER
password: 'secret', database: 'my_db', //env var: PGDATABASE
database: 'my_db', password: 'secret', //env var: PGPASSWORD
port: 5432 port: 5432, //env var: PGPORT
max: 10, // max number of clients in the pool
idleTimeoutMillis: 30000, // how long a client is allowed to remain idle before being closed
}; };
var pool = new Pool(config); var pool = new pg.Pool(config);
//this initializes a connection pool //this initializes a connection pool
//it will keep idle connections open for a (configurable) 30 seconds //it will keep idle connections open for a 30 seconds
//and set a limit of 10 (also configurable) //and set a limit of maximum 10 idle clients
pool.connect(function(err, client, done) { pool.connect(function(err, client, done) {
if(err) { if(err) {
return console.error('error fetching client from pool', err); return console.error('error fetching client from pool', err);
@ -50,38 +91,13 @@ pool.connect(function(err, client, done) {
}); });
``` ```
node-postgres uses [pg-pool](https://github.com/brianc/node-pg-pool.git) to manage pooling and only provides a very thin layer on top. node-postgres uses [pg-pool](https://github.com/brianc/node-pg-pool.git) to manage pooling and includes it and exports it for convienience. If you want, you can `require('pg-pool')` and use it directly - its the same as the constructor exported at `pg.Pool`.
It's _highly recommend_ you read the documentation for [pg-pool](https://github.com/brianc/node-pg-pool.git) It's __highly recommend__ you read the documentation for [pg-pool](https://github.com/brianc/node-pg-pool.git).
[Here is a tl;dr get up & running quickly example](https://github.com/brianc/node-postgres/wiki/Example) [Here is an up & running quickly example](https://github.com/brianc/node-postgres/wiki/Example)
### Client instance
Sometimes you may not want to use a pool of connections. You can easily connect a single client to a postgres instance, run some queries, and disconnect.
```javascript
var pg = require('pg');
var conString = "postgres://username:password@localhost/database";
var client = new pg.Client(conString);
client.connect(function(err) {
if(err) {
return console.error('could not connect to postgres', err);
}
client.query('SELECT NOW() AS "theTime"', function(err, result) {
if(err) {
return console.error('error running query', err);
}
console.log(result.rows[0].theTime);
//output: Tue Jan 15 2013 19:12:47 GMT-600 (CST)
client.end();
});
});
```
## [More Documentation](https://github.com/brianc/node-postgres/wiki) ## [More Documentation](https://github.com/brianc/node-postgres/wiki)
@ -94,16 +110,26 @@ $ npm install pg pg-native
``` ```
node-postgres contains a pure JavaScript protocol implementation which is quite fast, but you can optionally use native bindings for a 20-30% increase in parsing speed. Both versions are adequate for production workloads. node-postgres contains a pure JavaScript protocol implementation which is quite fast, but you can optionally use [native](https://github.com/brianc/node-pg-native) [bindings](https://github.com/brianc/node-libpq) for a 20-30% increase in parsing speed (YMMV). Both versions are adequate for production workloads. I personally use the pure JavaScript implementation because I like knowing whats going on all the way down to the binary on the socket, and it allows for some fancier [use](https://github.com/brianc/node-pg-cursor) [cases](https://github.com/brianc/node-pg-query-stream) which are difficult to do with libpq. :smile:
To use the native bindings, first install [pg-native](https://github.com/brianc/node-pg-native.git). Once pg-native is installed, simply replace `require('pg')` with `require('pg').native`. To use the native bindings, first install [pg-native](https://github.com/brianc/node-pg-native.git). Once pg-native is installed, simply replace `var pg = require('pg')` with `var pg = require('pg').native`. Make sure any exported constructors from `pg` are from the native instance. Example:
```js
var pg = require('pg').native
var Pool = require('pg').Pool // bad! this is not bound to the native client
var Client = require('pg').Client // bad! this is the pure JavaScript client
var pg = require('pg').native
var Pool = pg.Pool // good! a pool bound to the native client
var Client = pg.Client // good! this client uses libpq bindings
```
node-postgres abstracts over the pg-native module to provide exactly the same interface as the pure JavaScript version. Care has been taken to keep the number of api differences between the two modules to a minimum; however, it is recommend you use either the pure JavaScript or native bindings in both development and production and don't mix & match them in the same process - it can get confusing! node-postgres abstracts over the pg-native module to provide exactly the same interface as the pure JavaScript version. Care has been taken to keep the number of api differences between the two modules to a minimum; however, it is recommend you use either the pure JavaScript or native bindings in both development and production and don't mix & match them in the same process - it can get confusing!
## Features ## Features
* pure JavaScript client and native libpq bindings share _the same api_ * pure JavaScript client and native libpq bindings share _the same api_
* optional connection pooling * connection pooling
* extensible js<->postgresql data-type coercion * extensible js<->postgresql data-type coercion
* supported PostgreSQL features * supported PostgreSQL features
* parameterized queries * parameterized queries
@ -111,9 +137,14 @@ node-postgres abstracts over the pg-native module to provide exactly the same in
* async notifications with `LISTEN/NOTIFY` * async notifications with `LISTEN/NOTIFY`
* bulk import & export with `COPY TO/COPY FROM` * bulk import & export with `COPY TO/COPY FROM`
## Extras
node-postgres is by design pretty light on abstractions. These are some handy modules we've been using over the years to complete the picture.
Entire list can be found on [wiki](https://github.com/brianc/node-postgres/wiki/Extras)
## Contributing ## Contributing
__We love contributions!__ __:heart: contributions!__
If you need help getting the tests running locally or have any questions about the code when working on a patch please feel free to email me or gchat me. If you need help getting the tests running locally or have any questions about the code when working on a patch please feel free to email me or gchat me.
@ -135,17 +166,14 @@ If at all possible when you open an issue please provide
Usually I'll pop the code into the repo as a test. Hopefully the test fails. Then I make the test pass. Then everyone's happy! Usually I'll pop the code into the repo as a test. Hopefully the test fails. Then I make the test pass. Then everyone's happy!
If you need help or run into _any_ issues getting node-postgres to work on your system please report a bug or contact me directly. I am usually available via google-talk at my github account public email address. If you need help or run into _any_ issues getting node-postgres to work on your system please report a bug or contact me directly. I am usually available via google-talk at my github account public email address. Remember this is a labor of love, and though I try to get back to everything sometimes life takes priority, and I might take a while. It helps if you use nice code formatting in your issue, search for existing answers before posting, and come back and close out the issue if you figure out a solution. The easier you can make it for me, the quicker I'll try and respond to you!
If you need deeper support, have application specific questions, would like to sponsor development, or want consulting around node & postgres please send me an email, I'm always happy to discuss!
I usually tweet about any important status updates or changes to node-postgres on twitter. I usually tweet about any important status updates or changes to node-postgres on twitter.
Follow me [@briancarlson](https://twitter.com/briancarlson) to keep up to date. Follow me [@briancarlson](https://twitter.com/briancarlson) to keep up to date.
## Extras
node-postgres is by design pretty light on abstractions. These are some handy modules we've been using over the years to complete the picture.
Entire list can be found on [wiki](https://github.com/brianc/node-postgres/wiki/Extras)
## License ## License
Copyright (c) 2010-2016 Brian Carlson (brian.m.carlson@gmail.com) Copyright (c) 2010-2016 Brian Carlson (brian.m.carlson@gmail.com)

View File

@ -4,14 +4,14 @@ var Client = require('./client');
var defaults = require('./defaults'); var defaults = require('./defaults');
var Connection = require('./connection'); var Connection = require('./connection');
var ConnectionParameters = require('./connection-parameters'); var ConnectionParameters = require('./connection-parameters');
var Pool = require('pg-pool'); var poolFactory = require('./pool-factory');
var PG = function(clientConstructor) { var PG = function(clientConstructor) {
EventEmitter.call(this); EventEmitter.call(this);
this.defaults = defaults; this.defaults = defaults;
this.Client = clientConstructor; this.Client = clientConstructor;
this.Query = this.Client.Query; this.Query = this.Client.Query;
this.Pool = Pool; this.Pool = poolFactory(this.Client);
this._pools = []; this._pools = [];
this.Connection = Connection; this.Connection = Connection;
this.types = require('pg-types'); this.types = require('pg-types');
@ -58,7 +58,7 @@ PG.prototype.connect = function(config, callback) {
config.idleTimeoutMillis = config.idleTimeoutMillis || config.poolIdleTimeout || defaults.poolIdleTimeout; config.idleTimeoutMillis = config.idleTimeoutMillis || config.poolIdleTimeout || defaults.poolIdleTimeout;
config.log = config.log || config.poolLog || defaults.poolLog; config.log = config.log || config.poolLog || defaults.poolLog;
this._pools[poolName] = this._pools[poolName] || new Pool(config, this.Client); this._pools[poolName] = this._pools[poolName] || new this.Pool(config);
var pool = this._pools[poolName]; var pool = this._pools[poolName];
pool.connect(callback); pool.connect(callback);
if(!pool.listeners('error').length) { if(!pool.listeners('error').length) {
@ -69,7 +69,7 @@ PG.prototype.connect = function(config, callback) {
} }
}; };
// cancel the query runned by the given client // cancel the query running on the given client
PG.prototype.cancel = function(config, client, query) { PG.prototype.cancel = function(config, client, query) {
if(client.native) { if(client.native) {
return client.cancel(query); return client.cancel(query);

18
lib/pool-factory.js Normal file
View File

@ -0,0 +1,18 @@
var Client = require('./client');
var util = require('util');
var Pool = require('pg-pool');
module.exports = function(Client) {
var BoundPool = function(options) {
var config = { Client: Client };
for (var key in options) {
config[key] = options[key];
}
Pool.call(this, config);
};
util.inherits(BoundPool, Pool);
return BoundPool;
};

View File

@ -21,6 +21,12 @@ test('api', function() {
pg.connect(helper.config, assert.calls(function(err, client, done) { pg.connect(helper.config, assert.calls(function(err, client, done) {
assert.equal(err, null, "Failed to connect: " + helper.sys.inspect(err)); assert.equal(err, null, "Failed to connect: " + helper.sys.inspect(err));
if (helper.args.native) {
assert(client.native)
} else {
assert(!client.native)
}
client.query('CREATE TEMP TABLE band(name varchar(100))'); client.query('CREATE TEMP TABLE band(name varchar(100))');
['the flaming lips', 'wolf parade', 'radiohead', 'bright eyes', 'the beach boys', 'dead black hearts'].forEach(function(bandName) { ['the flaming lips', 'wolf parade', 'radiohead', 'bright eyes', 'the beach boys', 'dead black hearts'].forEach(function(bandName) {

View File

@ -25,4 +25,4 @@ test('connection with config ssl = true', function() {
pg.end(); pg.end();
})) }))
})); }));
}); });

View File

@ -1,6 +1,5 @@
var helper = require(__dirname + "/../test-helper"); var helper = require(__dirname + "/../test-helper");
var pg = require(__dirname + "/../../../lib"); var pg = require(__dirname + "/../../../lib");
pg = pg;
//first make pool hold 2 clients //first make pool hold 2 clients
pg.defaults.poolSize = 2; pg.defaults.poolSize = 2;

View File

@ -0,0 +1,15 @@
var helper = require(__dirname + "/../test-helper")
var pg = helper.pg
var native = helper.args.native
var pool = new pg.Pool()
pool.connect(assert.calls(function(err, client, done) {
if (native) {
assert(client.native)
} else {
assert(!client.native)
}
done()
pool.end()
}))