Skip to content

Commit f628742

Browse files
Merge branch 'daniel-liu-bitio-dliu/pg-browser'
2 parents 7cbca13 + 8df77ee commit f628742

38 files changed

+435
-86483
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: CI
22

3-
on: [push]
3+
on: [push, pull_request]
44

55
jobs:
66
build:

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ For richer information consult the commit log on github with referenced pull req
44

55
We do not include break-fix version release in this file.
66

7+
### pg-pool@3.5.0
8+
9+
- Add connection [lifetime limit](https://github.com/brianc/node-postgres/pull/2698) config option.
10+
711
### pg@8.7.0
812

913
- Add optional config to [pool](https://github.com/brianc/node-postgres/pull/2568) to allow process to exit if pool is idle.

package.json

+7-1
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,19 @@
2626
"eslint-plugin-prettier": "^3.1.4",
2727
"lerna": "^4.0.0",
2828
"prettier": "2.1.2",
29-
"typescript": "^4.0.3"
29+
"typescript": "^4.0.3",
30+
"webpack-cli": "^4.9.2"
3031
},
3132
"prettier": {
3233
"semi": false,
3334
"printWidth": 120,
3435
"arrowParens": "always",
3536
"trailingComma": "es5",
3637
"singleQuote": true
38+
},
39+
"dependencies": {
40+
"is-ip": "^4.0.0",
41+
"pg": "^8.7.3",
42+
"websocket-stream": "^5.5.2"
3743
}
3844
}

packages/pg-cursor/index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -229,9 +229,9 @@ class Cursor extends EventEmitter {
229229

230230
_close_checkFinished(cb) {
231231
this._closePortal()
232-
// try to delay the callback until portal is finished closing
232+
// delay the callback until portal is finished closing
233233
// (this means a closeComplete followed by a readyForQuery)
234-
// (it's possible to have a readyForQuery without a closeComplete if called from moveAllAndClose)
234+
// (when moveAllAndClose calls closePortal, client will receive a readyForQuery generated by "MOVE ALL" simple query before receiving a closeComplete and a subsequent readyForQuery)
235235
var finishedClosing = false
236236
this.connection.once('closeComplete', function () {
237237
finishedClosing = true

packages/pg-cursor/package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "pg-cursor",
3-
"version": "2.7.1",
3+
"version": "2.7.3",
44
"description": "Query cursor extension for node-postgres",
55
"main": "index.js",
66
"directories": {
@@ -17,8 +17,8 @@
1717
"author": "Brian M. Carlson",
1818
"license": "MIT",
1919
"devDependencies": {
20-
"mocha": "^9.2.0",
21-
"pg": "^8.7.1"
20+
"mocha": "^9.2.1",
21+
"pg": "^8.7.3"
2222
},
2323
"peerDependencies": {
2424
"pg": "^8"

packages/pg-pool/index.js

+32
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ class Pool extends EventEmitter {
8484
this.options.max = this.options.max || this.options.poolSize || 10
8585
this.options.maxUses = this.options.maxUses || Infinity
8686
this.options.allowExitOnIdle = this.options.allowExitOnIdle || false
87+
this.options.maxLifetimeSeconds = this.options.maxLifetimeSeconds || 0
8788
this.log = this.options.log || function () {}
8889
this.Client = this.options.Client || Client || require('../pg/lib/index.js').Client
8990
this.Promise = this.options.Promise || global.Promise
@@ -94,6 +95,7 @@ class Pool extends EventEmitter {
9495

9596
this._clients = []
9697
this._idle = []
98+
this._expired = new WeakSet()
9799
this._pendingQueue = []
98100
this._endCallback = undefined
99101
this.ending = false
@@ -123,6 +125,7 @@ class Pool extends EventEmitter {
123125
}
124126
return
125127
}
128+
126129
// if we don't have any waiting, do nothing
127130
if (!this._pendingQueue.length) {
128131
this.log('no queued requests')
@@ -248,6 +251,22 @@ class Pool extends EventEmitter {
248251
} else {
249252
this.log('new client connected')
250253

254+
if (this.options.maxLifetimeSeconds !== 0) {
255+
setTimeout(() => {
256+
this.log('ending client due to expired lifetime')
257+
this._expired.add(client)
258+
const idleIndex = this._idle.findIndex((idleItem) => idleItem.client === client)
259+
if (idleIndex !== -1) {
260+
this._acquireClient(
261+
client,
262+
new PendingItem((err, client, clientRelease) => clientRelease()),
263+
idleListener,
264+
false
265+
)
266+
}
267+
}, this.options.maxLifetimeSeconds * 1000)
268+
}
269+
251270
return this._acquireClient(client, pendingItem, idleListener, true)
252271
}
253272
})
@@ -318,6 +337,15 @@ class Pool extends EventEmitter {
318337
return
319338
}
320339

340+
const isExpired = this._expired.has(client)
341+
if (isExpired) {
342+
this.log('remove expired client')
343+
this._expired.delete(client)
344+
this._remove(client)
345+
this._pulseQueue()
346+
return
347+
}
348+
321349
// idle timeout
322350
let tid
323351
if (this.options.idleTimeoutMillis) {
@@ -414,6 +442,10 @@ class Pool extends EventEmitter {
414442
return this._idle.length
415443
}
416444

445+
get expiredCount() {
446+
return this._clients.reduce((acc, client) => acc + (this._expired.has(client) ? 1 : 0), 0)
447+
}
448+
417449
get totalCount() {
418450
return this._clients.length
419451
}

packages/pg-pool/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "pg-pool",
3-
"version": "3.4.1",
3+
"version": "3.5.1",
44
"description": "Connection pool for node-postgres",
55
"main": "index.js",
66
"directories": {
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict'
2+
const co = require('co')
3+
const expect = require('expect.js')
4+
5+
const describe = require('mocha').describe
6+
const it = require('mocha').it
7+
const path = require('path')
8+
9+
const Pool = require('../')
10+
11+
describe('lifetime timeout', () => {
12+
it('connection lifetime should expire and remove the client', (done) => {
13+
const pool = new Pool({ maxLifetimeSeconds: 1 })
14+
pool.query('SELECT NOW()')
15+
pool.on('remove', () => {
16+
console.log('expired while idle - on-remove event')
17+
expect(pool.expiredCount).to.equal(0)
18+
expect(pool.totalCount).to.equal(0)
19+
done()
20+
})
21+
})
22+
it('connection lifetime should expire and remove the client after the client is done working', (done) => {
23+
const pool = new Pool({ maxLifetimeSeconds: 1 })
24+
pool.query('SELECT pg_sleep(1.01)')
25+
pool.on('remove', () => {
26+
console.log('expired while busy - on-remove event')
27+
expect(pool.expiredCount).to.equal(0)
28+
expect(pool.totalCount).to.equal(0)
29+
done()
30+
})
31+
})
32+
it(
33+
'can remove expired clients and recreate them',
34+
co.wrap(function* () {
35+
const pool = new Pool({ maxLifetimeSeconds: 1 })
36+
let query = pool.query('SELECT pg_sleep(1)')
37+
expect(pool.expiredCount).to.equal(0)
38+
expect(pool.totalCount).to.equal(1)
39+
yield query
40+
expect(pool.expiredCount).to.equal(0)
41+
expect(pool.totalCount).to.equal(0)
42+
yield pool.query('SELECT NOW()')
43+
expect(pool.expiredCount).to.equal(0)
44+
expect(pool.totalCount).to.equal(1)
45+
})
46+
)
47+
})
+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/// <reference types="node" />
2+
export declare type Mode = 'text' | 'binary';
3+
export declare type MessageName = 'parseComplete' | 'bindComplete' | 'closeComplete' | 'noData' | 'portalSuspended' | 'replicationStart' | 'emptyQuery' | 'copyDone' | 'copyData' | 'rowDescription' | 'parameterDescription' | 'parameterStatus' | 'backendKeyData' | 'notification' | 'readyForQuery' | 'commandComplete' | 'dataRow' | 'copyInResponse' | 'copyOutResponse' | 'authenticationOk' | 'authenticationMD5Password' | 'authenticationCleartextPassword' | 'authenticationSASL' | 'authenticationSASLContinue' | 'authenticationSASLFinal' | 'error' | 'notice';
4+
export interface BackendMessage {
5+
name: MessageName;
6+
length: number;
7+
}
8+
export declare const parseComplete: BackendMessage;
9+
export declare const bindComplete: BackendMessage;
10+
export declare const closeComplete: BackendMessage;
11+
export declare const noData: BackendMessage;
12+
export declare const portalSuspended: BackendMessage;
13+
export declare const replicationStart: BackendMessage;
14+
export declare const emptyQuery: BackendMessage;
15+
export declare const copyDone: BackendMessage;
16+
interface NoticeOrError {
17+
message: string | undefined;
18+
severity: string | undefined;
19+
code: string | undefined;
20+
detail: string | undefined;
21+
hint: string | undefined;
22+
position: string | undefined;
23+
internalPosition: string | undefined;
24+
internalQuery: string | undefined;
25+
where: string | undefined;
26+
schema: string | undefined;
27+
table: string | undefined;
28+
column: string | undefined;
29+
dataType: string | undefined;
30+
constraint: string | undefined;
31+
file: string | undefined;
32+
line: string | undefined;
33+
routine: string | undefined;
34+
}
35+
export declare class DatabaseError extends Error implements NoticeOrError {
36+
readonly length: number;
37+
readonly name: MessageName;
38+
severity: string | undefined;
39+
code: string | undefined;
40+
detail: string | undefined;
41+
hint: string | undefined;
42+
position: string | undefined;
43+
internalPosition: string | undefined;
44+
internalQuery: string | undefined;
45+
where: string | undefined;
46+
schema: string | undefined;
47+
table: string | undefined;
48+
column: string | undefined;
49+
dataType: string | undefined;
50+
constraint: string | undefined;
51+
file: string | undefined;
52+
line: string | undefined;
53+
routine: string | undefined;
54+
constructor(message: string, length: number, name: MessageName);
55+
}
56+
export declare class CopyDataMessage {
57+
readonly length: number;
58+
readonly chunk: Buffer;
59+
readonly name = "copyData";
60+
constructor(length: number, chunk: Buffer);
61+
}
62+
export declare class CopyResponse {
63+
readonly length: number;
64+
readonly name: MessageName;
65+
readonly binary: boolean;
66+
readonly columnTypes: number[];
67+
constructor(length: number, name: MessageName, binary: boolean, columnCount: number);
68+
}
69+
export declare class Field {
70+
readonly name: string;
71+
readonly tableID: number;
72+
readonly columnID: number;
73+
readonly dataTypeID: number;
74+
readonly dataTypeSize: number;
75+
readonly dataTypeModifier: number;
76+
readonly format: Mode;
77+
constructor(name: string, tableID: number, columnID: number, dataTypeID: number, dataTypeSize: number, dataTypeModifier: number, format: Mode);
78+
}
79+
export declare class RowDescriptionMessage {
80+
readonly length: number;
81+
readonly fieldCount: number;
82+
readonly name: MessageName;
83+
readonly fields: Field[];
84+
constructor(length: number, fieldCount: number);
85+
}
86+
export declare class ParameterDescriptionMessage {
87+
readonly length: number;
88+
readonly parameterCount: number;
89+
readonly name: MessageName;
90+
readonly dataTypeIDs: number[];
91+
constructor(length: number, parameterCount: number);
92+
}
93+
export declare class ParameterStatusMessage {
94+
readonly length: number;
95+
readonly parameterName: string;
96+
readonly parameterValue: string;
97+
readonly name: MessageName;
98+
constructor(length: number, parameterName: string, parameterValue: string);
99+
}
100+
export declare class AuthenticationMD5Password implements BackendMessage {
101+
readonly length: number;
102+
readonly salt: Buffer;
103+
readonly name: MessageName;
104+
constructor(length: number, salt: Buffer);
105+
}
106+
export declare class BackendKeyDataMessage {
107+
readonly length: number;
108+
readonly processID: number;
109+
readonly secretKey: number;
110+
readonly name: MessageName;
111+
constructor(length: number, processID: number, secretKey: number);
112+
}
113+
export declare class NotificationResponseMessage {
114+
readonly length: number;
115+
readonly processId: number;
116+
readonly channel: string;
117+
readonly payload: string;
118+
readonly name: MessageName;
119+
constructor(length: number, processId: number, channel: string, payload: string);
120+
}
121+
export declare class ReadyForQueryMessage {
122+
readonly length: number;
123+
readonly status: string;
124+
readonly name: MessageName;
125+
constructor(length: number, status: string);
126+
}
127+
export declare class CommandCompleteMessage {
128+
readonly length: number;
129+
readonly text: string;
130+
readonly name: MessageName;
131+
constructor(length: number, text: string);
132+
}
133+
export declare class DataRowMessage {
134+
length: number;
135+
fields: any[];
136+
readonly fieldCount: number;
137+
readonly name: MessageName;
138+
constructor(length: number, fields: any[]);
139+
}
140+
export declare class NoticeMessage implements BackendMessage, NoticeOrError {
141+
readonly length: number;
142+
readonly message: string | undefined;
143+
constructor(length: number, message: string | undefined);
144+
readonly name = "notice";
145+
severity: string | undefined;
146+
code: string | undefined;
147+
detail: string | undefined;
148+
hint: string | undefined;
149+
position: string | undefined;
150+
internalPosition: string | undefined;
151+
internalQuery: string | undefined;
152+
where: string | undefined;
153+
schema: string | undefined;
154+
table: string | undefined;
155+
column: string | undefined;
156+
dataType: string | undefined;
157+
constraint: string | undefined;
158+
file: string | undefined;
159+
line: string | undefined;
160+
routine: string | undefined;
161+
}
162+
export {};

packages/pg-protocol/tsconfig.json

+3
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,8 @@
2121
},
2222
"include": [
2323
"src/**/*"
24+
],
25+
"exclude": [
26+
"dist"
2427
]
2528
}

packages/pg-query-stream/package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "pg-query-stream",
3-
"version": "4.2.1",
3+
"version": "4.2.3",
44
"description": "Postgres query result returned as readable stream",
55
"main": "./dist/index.js",
66
"types": "./dist/index.d.ts",
@@ -37,13 +37,13 @@
3737
"eslint-plugin-promise": "^3.5.0",
3838
"JSONStream": "~0.7.1",
3939
"mocha": "^9.2.0",
40-
"pg": "^8.7.1",
40+
"pg": "^8.7.3",
4141
"stream-spec": "~0.3.5",
4242
"stream-tester": "0.0.5",
4343
"ts-node": "^8.5.4",
4444
"typescript": "^4.0.3"
4545
},
4646
"dependencies": {
47-
"pg-cursor": "^2.7.1"
47+
"pg-cursor": "^2.7.3"
4848
}
4949
}

0 commit comments

Comments
 (0)