1 | 'use strict'
|
2 | const net = require('net')
|
3 | const co = require('co')
|
4 | const expect = require('expect.js')
|
5 |
|
6 | const describe = require('mocha').describe
|
7 | const it = require('mocha').it
|
8 |
|
9 | const Pool = require('../')
|
10 |
|
11 | describe('pool error handling', function () {
|
12 | it('Should complete these queries without dying', function (done) {
|
13 | const pool = new Pool()
|
14 | let errors = 0
|
15 | let shouldGet = 0
|
16 | function runErrorQuery() {
|
17 | shouldGet++
|
18 | return new Promise(function (resolve, reject) {
|
19 | pool.query("SELECT 'asd'+1 ").then(function (res) {
|
20 | reject(res)
|
21 | }).catch(function (err) {
|
22 | errors++
|
23 | resolve(err)
|
24 | })
|
25 | })
|
26 | }
|
27 | const ps = []
|
28 | for (let i = 0; i < 5; i++) {
|
29 | ps.push(runErrorQuery())
|
30 | }
|
31 | Promise.all(ps).then(function () {
|
32 | expect(shouldGet).to.eql(errors)
|
33 | pool.end(done)
|
34 | })
|
35 | })
|
36 |
|
37 | describe('calling release more than once', () => {
|
38 | it('should throw each time', co.wrap(function* () {
|
39 | const pool = new Pool()
|
40 | const client = yield pool.connect()
|
41 | client.release()
|
42 | expect(() => client.release()).to.throwError()
|
43 | expect(() => client.release()).to.throwError()
|
44 | return yield pool.end()
|
45 | }))
|
46 |
|
47 | it('should throw each time with callbacks', function (done) {
|
48 | const pool = new Pool()
|
49 |
|
50 | pool.connect(function (err, client, clientDone) {
|
51 | expect(err).not.to.be.an(Error)
|
52 | clientDone()
|
53 |
|
54 | expect(() => clientDone()).to.throwError()
|
55 | expect(() => clientDone()).to.throwError()
|
56 |
|
57 | pool.end(done)
|
58 | })
|
59 | })
|
60 | })
|
61 |
|
62 | describe('calling connect after end', () => {
|
63 | it('should return an error', function* () {
|
64 | const pool = new Pool()
|
65 | const res = yield pool.query('SELECT $1::text as name', ['hi'])
|
66 | expect(res.rows[0].name).to.equal('hi')
|
67 | const wait = pool.end()
|
68 | pool.query('select now()')
|
69 | yield wait
|
70 | expect(() => pool.query('select now()')).to.reject()
|
71 | })
|
72 | })
|
73 |
|
74 | describe('using an ended pool', () => {
|
75 | it('rejects all additional promises', (done) => {
|
76 | const pool = new Pool()
|
77 | const promises = []
|
78 | pool.end()
|
79 | .then(() => {
|
80 | const squash = promise => promise.catch(e => 'okay!')
|
81 | promises.push(squash(pool.connect()))
|
82 | promises.push(squash(pool.query('SELECT NOW()')))
|
83 | promises.push(squash(pool.end()))
|
84 | Promise.all(promises).then(res => {
|
85 | expect(res).to.eql(['okay!', 'okay!', 'okay!'])
|
86 | done()
|
87 | })
|
88 | })
|
89 | })
|
90 |
|
91 | it('returns an error on all additional callbacks', (done) => {
|
92 | const pool = new Pool()
|
93 | pool.end(() => {
|
94 | pool.query('SELECT *', (err) => {
|
95 | expect(err).to.be.an(Error)
|
96 | pool.connect((err) => {
|
97 | expect(err).to.be.an(Error)
|
98 | pool.end((err) => {
|
99 | expect(err).to.be.an(Error)
|
100 | done()
|
101 | })
|
102 | })
|
103 | })
|
104 | })
|
105 | })
|
106 | })
|
107 |
|
108 | describe('error from idle client', () => {
|
109 | it('removes client from pool', co.wrap(function* () {
|
110 | const pool = new Pool()
|
111 | const client = yield pool.connect()
|
112 | expect(pool.totalCount).to.equal(1)
|
113 | expect(pool.waitingCount).to.equal(0)
|
114 | expect(pool.idleCount).to.equal(0)
|
115 | client.release()
|
116 | yield new Promise((resolve, reject) => {
|
117 | process.nextTick(() => {
|
118 | let poolError
|
119 | pool.once('error', (err) => {
|
120 | poolError = err
|
121 | })
|
122 |
|
123 | let clientError
|
124 | client.once('error', (err) => {
|
125 | clientError = err
|
126 | })
|
127 |
|
128 | client.emit('error', new Error('expected'))
|
129 |
|
130 | expect(clientError.message).to.equal('expected')
|
131 | expect(poolError.message).to.equal('expected')
|
132 | expect(pool.idleCount).to.equal(0)
|
133 | expect(pool.totalCount).to.equal(0)
|
134 | pool.end().then(resolve, reject)
|
135 | })
|
136 | })
|
137 | }))
|
138 | })
|
139 |
|
140 | describe('error from in-use client', () => {
|
141 | it('keeps the client in the pool', co.wrap(function* () {
|
142 | const pool = new Pool()
|
143 | const client = yield pool.connect()
|
144 | expect(pool.totalCount).to.equal(1)
|
145 | expect(pool.waitingCount).to.equal(0)
|
146 | expect(pool.idleCount).to.equal(0)
|
147 |
|
148 | yield new Promise((resolve, reject) => {
|
149 | process.nextTick(() => {
|
150 | let poolError
|
151 | pool.once('error', (err) => {
|
152 | poolError = err
|
153 | })
|
154 |
|
155 | let clientError
|
156 | client.once('error', (err) => {
|
157 | clientError = err
|
158 | })
|
159 |
|
160 | client.emit('error', new Error('expected'))
|
161 |
|
162 | expect(clientError.message).to.equal('expected')
|
163 | expect(poolError).not.to.be.ok()
|
164 | expect(pool.idleCount).to.equal(0)
|
165 | expect(pool.totalCount).to.equal(1)
|
166 | client.release()
|
167 | pool.end().then(resolve, reject)
|
168 | })
|
169 | })
|
170 | }))
|
171 | })
|
172 |
|
173 | describe('passing a function to pool.query', () => {
|
174 | it('calls back with error', (done) => {
|
175 | const pool = new Pool()
|
176 | console.log('passing fn to query')
|
177 | pool.query((err) => {
|
178 | expect(err).to.be.an(Error)
|
179 | pool.end(done)
|
180 | })
|
181 | })
|
182 | })
|
183 |
|
184 | describe('pool with lots of errors', () => {
|
185 | it('continues to work and provide new clients', co.wrap(function* () {
|
186 | const pool = new Pool({ max: 1 })
|
187 | const errors = []
|
188 | for (var i = 0; i < 20; i++) {
|
189 | try {
|
190 | yield pool.query('invalid sql')
|
191 | } catch (err) {
|
192 | errors.push(err)
|
193 | }
|
194 | }
|
195 | expect(errors).to.have.length(20)
|
196 | expect(pool.idleCount).to.equal(0)
|
197 | expect(pool.query).to.be.a(Function)
|
198 | const res = yield pool.query('SELECT $1::text as name', ['brianc'])
|
199 | expect(res.rows).to.have.length(1)
|
200 | expect(res.rows[0].name).to.equal('brianc')
|
201 | return pool.end()
|
202 | }))
|
203 | })
|
204 |
|
205 | it('should continue with queued items after a connection failure', (done) => {
|
206 | const closeServer = net.createServer((socket) => {
|
207 | socket.destroy()
|
208 | }).unref()
|
209 |
|
210 | closeServer.listen(() => {
|
211 | const pool = new Pool({ max: 1, port: closeServer.address().port, host: 'localhost' })
|
212 | pool.connect((err) => {
|
213 | expect(err).to.be.an(Error)
|
214 | if (err.errno) {
|
215 | expect(err.errno).to.be('ECONNRESET')
|
216 | }
|
217 | })
|
218 | pool.connect((err) => {
|
219 | expect(err).to.be.an(Error)
|
220 | if (err.errno) {
|
221 | expect(err.errno).to.be('ECONNRESET')
|
222 | }
|
223 | closeServer.close(() => {
|
224 | pool.end(done)
|
225 | })
|
226 | })
|
227 | })
|
228 | })
|
229 |
|
230 | it('handles post-checkout client failures in pool.query', (done) => {
|
231 | const pool = new Pool({ max: 1 })
|
232 | pool.on('error', () => {
|
233 |
|
234 | })
|
235 | pool.query('SELECT pg_sleep(5)', [], (err) => {
|
236 | expect(err).to.be.an(Error)
|
237 | done()
|
238 | })
|
239 |
|
240 | setTimeout(() => {
|
241 | pool._clients[0].end()
|
242 | }, 1000)
|
243 | })
|
244 | })
|