UNPKG

6.22 kBJavaScriptView Raw
1'use strict'
2const net = require('net')
3const co = require('co')
4const expect = require('expect.js')
5
6const describe = require('mocha').describe
7const it = require('mocha').it
8const before = require('mocha').before
9const after = require('mocha').after
10
11const Pool = require('../')
12
13describe('connection timeout', () => {
14 const connectionFailure = new Error('Temporary connection failure')
15
16 before((done) => {
17 this.server = net.createServer((socket) => {
18 socket.on('data', () => {
19 // discard any buffered data or the server wont terminate
20 })
21 })
22
23 this.server.listen(() => {
24 this.port = this.server.address().port
25 done()
26 })
27 })
28
29 after((done) => {
30 this.server.close(done)
31 })
32
33 it('should callback with an error if timeout is passed', (done) => {
34 const pool = new Pool({ connectionTimeoutMillis: 10, port: this.port, host: 'localhost' })
35 pool.connect((err, client, release) => {
36 expect(err).to.be.an(Error)
37 expect(err.message).to.contain('timeout')
38 expect(client).to.equal(undefined)
39 expect(pool.idleCount).to.equal(0)
40 done()
41 })
42 })
43
44 it('should reject promise with an error if timeout is passed', (done) => {
45 const pool = new Pool({ connectionTimeoutMillis: 10, port: this.port, host: 'localhost' })
46 pool.connect().catch((err) => {
47 expect(err).to.be.an(Error)
48 expect(err.message).to.contain('timeout')
49 expect(pool.idleCount).to.equal(0)
50 done()
51 })
52 })
53
54 it(
55 'should handle multiple timeouts',
56 co.wrap(
57 function* () {
58 const errors = []
59 const pool = new Pool({ connectionTimeoutMillis: 1, port: this.port, host: 'localhost' })
60 for (var i = 0; i < 15; i++) {
61 try {
62 yield pool.connect()
63 } catch (e) {
64 errors.push(e)
65 }
66 }
67 expect(errors).to.have.length(15)
68 }.bind(this)
69 )
70 )
71
72 it('should timeout on checkout of used connection', (done) => {
73 const pool = new Pool({ connectionTimeoutMillis: 100, max: 1 })
74 pool.connect((err, client, release) => {
75 expect(err).to.be(undefined)
76 expect(client).to.not.be(undefined)
77 pool.connect((err, client) => {
78 expect(err).to.be.an(Error)
79 expect(client).to.be(undefined)
80 release()
81 pool.end(done)
82 })
83 })
84 })
85
86 it('should not break further pending checkouts on a timeout', (done) => {
87 const pool = new Pool({ connectionTimeoutMillis: 200, max: 1 })
88 pool.connect((err, client, releaseOuter) => {
89 expect(err).to.be(undefined)
90
91 pool.connect((err, client) => {
92 expect(err).to.be.an(Error)
93 expect(client).to.be(undefined)
94 releaseOuter()
95 })
96
97 setTimeout(() => {
98 pool.connect((err, client, releaseInner) => {
99 expect(err).to.be(undefined)
100 expect(client).to.not.be(undefined)
101 releaseInner()
102 pool.end(done)
103 })
104 }, 100)
105 })
106 })
107
108 it('should timeout on query if all clients are busy', (done) => {
109 const pool = new Pool({ connectionTimeoutMillis: 100, max: 1 })
110 pool.connect((err, client, release) => {
111 expect(err).to.be(undefined)
112 expect(client).to.not.be(undefined)
113 pool.query('select now()', (err, result) => {
114 expect(err).to.be.an(Error)
115 expect(result).to.be(undefined)
116 release()
117 pool.end(done)
118 })
119 })
120 })
121
122 it('should recover from timeout errors', (done) => {
123 const pool = new Pool({ connectionTimeoutMillis: 100, max: 1 })
124 pool.connect((err, client, release) => {
125 expect(err).to.be(undefined)
126 expect(client).to.not.be(undefined)
127 pool.query('select now()', (err, result) => {
128 expect(err).to.be.an(Error)
129 expect(result).to.be(undefined)
130 release()
131 pool.query('select $1::text as name', ['brianc'], (err, res) => {
132 expect(err).to.be(undefined)
133 expect(res.rows).to.have.length(1)
134 pool.end(done)
135 })
136 })
137 })
138 })
139
140 it('continues processing after a connection failure', (done) => {
141 const Client = require('pg').Client
142 const orgConnect = Client.prototype.connect
143 let called = false
144
145 Client.prototype.connect = function (cb) {
146 // Simulate a failure on first call
147 if (!called) {
148 called = true
149
150 return setTimeout(() => {
151 cb(connectionFailure)
152 }, 100)
153 }
154 // And pass-through the second call
155 orgConnect.call(this, cb)
156 }
157
158 const pool = new Pool({
159 Client: Client,
160 connectionTimeoutMillis: 1000,
161 max: 1,
162 })
163
164 pool.connect((err, client, release) => {
165 expect(err).to.be(connectionFailure)
166
167 pool.query('select $1::text as name', ['brianc'], (err, res) => {
168 expect(err).to.be(undefined)
169 expect(res.rows).to.have.length(1)
170 pool.end(done)
171 })
172 })
173 })
174
175 it('releases newly connected clients if the queued already timed out', (done) => {
176 const Client = require('pg').Client
177
178 const orgConnect = Client.prototype.connect
179
180 let connection = 0
181
182 Client.prototype.connect = function (cb) {
183 // Simulate a failure on first call
184 if (connection === 0) {
185 connection++
186
187 return setTimeout(() => {
188 cb(connectionFailure)
189 }, 300)
190 }
191
192 // And second connect taking > connection timeout
193 if (connection === 1) {
194 connection++
195
196 return setTimeout(() => {
197 orgConnect.call(this, cb)
198 }, 1000)
199 }
200
201 orgConnect.call(this, cb)
202 }
203
204 const pool = new Pool({
205 Client: Client,
206 connectionTimeoutMillis: 1000,
207 max: 1,
208 })
209
210 // Direct connect
211 pool.connect((err, client, release) => {
212 expect(err).to.be(connectionFailure)
213 })
214
215 // Queued
216 let called = 0
217 pool.connect((err, client, release) => {
218 // Verify the callback is only called once
219 expect(called++).to.be(0)
220 expect(err).to.be.an(Error)
221
222 pool.query('select $1::text as name', ['brianc'], (err, res) => {
223 expect(err).to.be(undefined)
224 expect(res.rows).to.have.length(1)
225 pool.end(done)
226 })
227 })
228 })
229})