Skip to content

Commit dd39e2f

Browse files
Konstantin MolchanovTotktonada
Konstantin Molchanov
authored andcommitted
Add timeout option for pool:get()
1 parent 2d22046 commit dd39e2f

File tree

3 files changed

+64
-14
lines changed

3 files changed

+64
-14
lines changed

README.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,15 +190,23 @@ Create a connection pool with count of size established connections.
190190
- `pool ~= nil` on success
191191
- `error(reason)` on error
192192

193-
### `conn = pool:get()`
193+
### `conn = pool:get(opts)`
194194

195195
Get a connection from pool. Reset connection before returning it. If connection
196-
is broken then it will be reestablished. If there is no free connections then
197-
calling fiber will sleep until another fiber returns some connection to pool.
196+
is broken then it will be reestablished.
197+
If there is no free connections and timeout is not specified then calling fiber
198+
will sleep until another fiber returns some connection to pool.
199+
If timeout is specified, and there is no free connections for the duration of the timeout,
200+
then the return value is nil.
201+
202+
*Options*:
203+
204+
- `timeout` - maximum number of seconds to wait for a connection
198205

199206
*Returns*:
200207

201-
- `conn ~= nil`
208+
- `conn ~= nil` on success
209+
- `conn == nil` on there is no free connections when timeout option is specified
202210

203211
### `pool:put(conn)`
204212

mysql/init.lua

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ local ffi = require('ffi')
77
local pool_mt
88
local conn_mt
99

10+
-- The marker for empty slots in a connection pool.
11+
--
12+
-- When a user puts a connection that is in an unusable state to a
13+
-- pool, we put this marker to a pool's internal connection queue.
14+
--
15+
-- Note: It should not be equal to `nil`, because fiber channel's
16+
-- `get` method returns `nil` when a timeout is reached.
17+
local POOL_EMPTY_SLOT = true
18+
1019
--create a new connection
1120
local function conn_create(mysql_conn)
1221
local queue = fiber.channel(1)
@@ -20,23 +29,28 @@ local function conn_create(mysql_conn)
2029
end
2130

2231
-- get connection from pool
23-
local function conn_get(pool)
24-
local mysql_conn = pool.queue:get()
32+
local function conn_get(pool, timeout)
33+
local mysql_conn = pool.queue:get(timeout)
34+
35+
-- A timeout was reached.
36+
if mysql_conn == nil then return nil end
37+
2538
local status
26-
if mysql_conn == nil then
39+
if mysql_conn == POOL_EMPTY_SLOT then
2740
status, mysql_conn = driver.connect(pool.host, pool.port or 0,
2841
pool.user, pool.pass,
2942
pool.db, pool.use_numeric_result)
3043
if status < 0 then
3144
return error(mysql_conn)
3245
end
3346
end
47+
3448
local conn = conn_create(mysql_conn)
3549
-- we can use ffi gc to return mysql connection to pool
3650
conn.__gc_hook = ffi.gc(ffi.new('void *'),
3751
function(self)
3852
mysql_conn:close()
39-
pool.queue:put(nil)
53+
pool.queue:put(POOL_EMPTY_SLOT)
4054
end)
4155
return conn
4256
end
@@ -46,7 +60,7 @@ local function conn_put(conn)
4660
ffi.gc(conn.__gc_hook, nil)
4761
if not conn.queue:get() then
4862
conn.usable = false
49-
return nil
63+
return POOL_EMPTY_SLOT
5064
end
5165
conn.usable = false
5266
return mysqlconn
@@ -171,19 +185,25 @@ local function pool_close(self)
171185
self.usable = false
172186
for i = 1, self.size do
173187
local mysql_conn = self.queue:get()
174-
if mysql_conn ~= nil then
188+
if mysql_conn ~= POOL_EMPTY_SLOT then
175189
mysql_conn:close()
176190
end
177191
end
178192
return 1
179193
end
180194

181195
-- Returns connection
182-
local function pool_get(self)
196+
local function pool_get(self, opts)
197+
opts = opts or {}
198+
183199
if not self.usable then
184200
return error('Pool is not usable')
185201
end
186-
local conn = conn_get(self)
202+
local conn = conn_get(self, opts.timeout)
203+
204+
-- A timeout was reached.
205+
if conn == nil then return nil end
206+
187207
conn:reset(self.user, self.pass, self.db)
188208
return conn
189209
end
@@ -193,7 +213,7 @@ local function pool_put(self, conn)
193213
if conn.usable then
194214
self.queue:put(conn_put(conn))
195215
else
196-
self.queue:put(nil)
216+
self.queue:put(POOL_EMPTY_SLOT)
197217
end
198218
end
199219

test/mysql.test.lua

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ local function test_mysql_int64(t, p)
171171
end
172172

173173
local function test_connection_pool(test, pool)
174-
test:plan(5)
174+
test:plan(6)
175175

176176
-- {{{ Case group: all connections are consumed initially.
177177

@@ -211,6 +211,28 @@ local function test_connection_pool(test, pool)
211211
assert(pool.queue:is_empty(), 'test case postcondition fails')
212212
end)
213213

214+
-- Case: get a connection with a timeout.
215+
test:test('pool:get({timeout = <...>})', function(test)
216+
test:plan(3)
217+
assert(pool.queue:is_empty(), 'test case precondition fails')
218+
219+
-- Verify that we blocks until reach a timeout, then
220+
-- unblocks and get `nil` as a result.
221+
local latch = fiber.channel(1)
222+
local conn
223+
fiber.create(function()
224+
conn = pool:get({timeout = 2})
225+
latch:put(true)
226+
end)
227+
local res = latch:get(1)
228+
test:is(res, nil, 'pool:get() blocks until a timeout')
229+
local res = latch:get()
230+
test:ok(res ~= nil, 'pool:get() unblocks after a timeout')
231+
test:is(conn, nil, 'pool:get() returns nil if a timeout was reached')
232+
233+
assert(pool.queue:is_empty(), 'test case postcondition fails')
234+
end)
235+
214236
-- Give all connections back to the poll.
215237
for _, conn in ipairs(connections) do
216238
pool:put(conn)

0 commit comments

Comments
 (0)