UNPKG

6.15 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('should handle multiple timeouts', co.wrap(function* () {
55 const errors = []
56 const pool = new Pool({ connectionTimeoutMillis: 1, port: this.port, host: 'localhost' })
57 for (var i = 0; i < 15; i++) {
58 try {
59 yield pool.connect()
60 } catch (e) {
61 errors.push(e)
62 }
63 }
64 expect(errors).to.have.length(15)
65 }.bind(this)))
66
67 it('should timeout on checkout of used connection', (done) => {
68 const pool = new Pool({ connectionTimeoutMillis: 100, max: 1 })
69 pool.connect((err, client, release) => {
70 expect(err).to.be(undefined)
71 expect(client).to.not.be(undefined)
72 pool.connect((err, client) => {
73 expect(err).to.be.an(Error)
74 expect(client).to.be(undefined)
75 release()
76 pool.end(done)
77 })
78 })
79 })
80
81 it('should not break further pending checkouts on a timeout', (done) => {
82 const pool = new Pool({ connectionTimeoutMillis: 200, max: 1 })
83 pool.connect((err, client, releaseOuter) => {
84 expect(err).to.be(undefined)
85
86 pool.connect((err, client) => {
87 expect(err).to.be.an(Error)
88 expect(client).to.be(undefined)
89 releaseOuter()
90 })
91
92 setTimeout(() => {
93 pool.connect((err, client, releaseInner) => {
94 expect(err).to.be(undefined)
95 expect(client).to.not.be(undefined)
96 releaseInner()
97 pool.end(done)
98 })
99 }, 100)
100 })
101 })
102
103 it('should timeout on query if all clients are busy', (done) => {
104 const pool = new Pool({ connectionTimeoutMillis: 100, max: 1 })
105 pool.connect((err, client, release) => {
106 expect(err).to.be(undefined)
107 expect(client).to.not.be(undefined)
108 pool.query('select now()', (err, result) => {
109 expect(err).to.be.an(Error)
110 expect(result).to.be(undefined)
111 release()
112 pool.end(done)
113 })
114 })
115 })
116
117 it('should recover from timeout errors', (done) => {
118 const pool = new Pool({ connectionTimeoutMillis: 100, max: 1 })
119 pool.connect((err, client, release) => {
120 expect(err).to.be(undefined)
121 expect(client).to.not.be(undefined)
122 pool.query('select now()', (err, result) => {
123 expect(err).to.be.an(Error)
124 expect(result).to.be(undefined)
125 release()
126 pool.query('select $1::text as name', ['brianc'], (err, res) => {
127 expect(err).to.be(undefined)
128 expect(res.rows).to.have.length(1)
129 pool.end(done)
130 })
131 })
132 })
133 })
134
135 it('continues processing after a connection failure', (done) => {
136 const Client = require('pg').Client
137 const orgConnect = Client.prototype.connect
138 let called = false
139
140 Client.prototype.connect = function (cb) {
141 // Simulate a failure on first call
142 if (!called) {
143 called = true
144
145 return setTimeout(() => {
146 cb(connectionFailure)
147 }, 100)
148 }
149 // And pass-through the second call
150 orgConnect.call(this, cb)
151 }
152
153 const pool = new Pool({
154 Client: Client,
155 connectionTimeoutMillis: 1000,
156 max: 1
157 })
158
159 pool.connect((err, client, release) => {
160 expect(err).to.be(connectionFailure)
161
162 pool.query('select $1::text as name', ['brianc'], (err, res) => {
163 expect(err).to.be(undefined)
164 expect(res.rows).to.have.length(1)
165 pool.end(done)
166 })
167 })
168 })
169
170 it('releases newly connected clients if the queued already timed out', (done) => {
171 const Client = require('pg').Client
172
173 const orgConnect = Client.prototype.connect
174
175 let connection = 0
176
177 Client.prototype.connect = function (cb) {
178 // Simulate a failure on first call
179 if (connection === 0) {
180 connection++
181
182 return setTimeout(() => {
183 cb(connectionFailure)
184 }, 300)
185 }
186
187 // And second connect taking > connection timeout
188 if (connection === 1) {
189 connection++
190
191 return setTimeout(() => {
192 orgConnect.call(this, cb)
193 }, 1000)
194 }
195
196 orgConnect.call(this, cb)
197 }
198
199 const pool = new Pool({
200 Client: Client,
201 connectionTimeoutMillis: 1000,
202 max: 1
203 })
204
205 // Direct connect
206 pool.connect((err, client, release) => {
207 expect(err).to.be(connectionFailure)
208 })
209
210 // Queued
211 let called = 0
212 pool.connect((err, client, release) => {
213 // Verify the callback is only called once
214 expect(called++).to.be(0)
215 expect(err).to.be.an(Error)
216
217 pool.query('select $1::text as name', ['brianc'], (err, res) => {
218 expect(err).to.be(undefined)
219 expect(res.rows).to.have.length(1)
220 pool.end(done)
221 })
222 })
223 })
224})