Skip to content

Commit b52f95b

Browse files
authored
fix(upstream): should not override default keepalive value (#5054)
Signed-off-by: spacewander <[email protected]>
1 parent cd97d82 commit b52f95b

File tree

4 files changed

+241
-2
lines changed

4 files changed

+241
-2
lines changed

apisix/balancer.lua

+20-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ local balancer = require("ngx.balancer")
1919
local core = require("apisix.core")
2020
local priority_balancer = require("apisix.balancer.priority")
2121
local ipairs = ipairs
22-
local enable_keepalive = balancer.enable_keepalive
22+
local is_http = ngx.config.subsystem == "http"
23+
local enable_keepalive = balancer.enable_keepalive and is_http
2324
local set_more_tries = balancer.set_more_tries
2425
local get_last_failure = balancer.get_last_failure
2526
local set_timeouts = balancer.set_timeouts
@@ -261,12 +262,29 @@ _M.pick_server = pick_server
261262
local set_current_peer
262263
do
263264
local pool_opt = {}
265+
local default_keepalive_pool
264266

265267
function set_current_peer(server, ctx)
266268
local up_conf = ctx.upstream_conf
267269
local keepalive_pool = up_conf.keepalive_pool
268270

269-
if keepalive_pool and enable_keepalive then
271+
if enable_keepalive then
272+
if not keepalive_pool then
273+
if not default_keepalive_pool then
274+
local local_conf = core.config.local_conf()
275+
local up_keepalive_conf =
276+
core.table.try_read_attr(local_conf, "nginx_config",
277+
"http", "upstream")
278+
default_keepalive_pool = {}
279+
default_keepalive_pool.idle_timeout =
280+
core.config_util.parse_time_unit(up_keepalive_conf.keepalive_timeout)
281+
default_keepalive_pool.size = up_keepalive_conf.keepalive
282+
default_keepalive_pool.requests = up_keepalive_conf.keepalive_requests
283+
end
284+
285+
keepalive_pool = default_keepalive_pool
286+
end
287+
270288
local idle_timeout = keepalive_pool.idle_timeout
271289
local size = keepalive_pool.size
272290
local requests = keepalive_pool.requests

apisix/core/config_util.lua

+89
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
-- limitations under the License.
1616
--
1717
local core_tab = require("apisix.core.table")
18+
local str_byte = string.byte
19+
local str_char = string.char
1820
local setmetatable = setmetatable
21+
local tostring = tostring
1922
local type = type
2023

2124

@@ -65,4 +68,90 @@ function _M.cancel_clean_handler(item, idx, fire)
6568
end
6669

6770

71+
-- Time intervals can be specified in milliseconds, seconds, minutes, hours, days and so on,
72+
-- using the following suffixes:
73+
-- ms milliseconds
74+
-- s seconds
75+
-- m minutes
76+
-- h hours
77+
-- d days
78+
-- w weeks
79+
-- M months, 30 days
80+
-- y years, 365 days
81+
-- Multiple units can be combined in a single value by specifying them in the order from the most
82+
-- to the least significant, and optionally separated by whitespace.
83+
-- A value without a suffix means seconds.
84+
function _M.parse_time_unit(s)
85+
local typ = type(s)
86+
if typ == "number" then
87+
return s
88+
end
89+
90+
if typ ~= "string" or #s == 0 then
91+
return nil, "invalid data: " .. tostring(s)
92+
end
93+
94+
local size = 0
95+
local size_in_unit = 0
96+
local step = 60 * 60 * 24 * 365
97+
local with_ms = false
98+
for i = 1, #s do
99+
local scale
100+
local unit = str_byte(s, i)
101+
if unit == 121 then -- y
102+
scale = 60 * 60 * 24 * 365
103+
elseif unit == 77 then -- M
104+
scale = 60 * 60 * 24 * 30
105+
elseif unit == 119 then -- w
106+
scale = 60 * 60 * 24 * 7
107+
elseif unit == 100 then -- d
108+
scale = 60 * 60 * 24
109+
elseif unit == 104 then -- h
110+
scale = 60 * 60
111+
elseif unit == 109 then -- m
112+
unit = str_byte(s, i + 1)
113+
if unit == 115 then -- ms
114+
size = size * 1000
115+
with_ms = true
116+
step = 0
117+
break
118+
end
119+
120+
scale = 60
121+
122+
elseif unit == 115 then -- s
123+
scale = 1
124+
elseif 48 <= unit and unit <= 57 then
125+
size_in_unit = size_in_unit * 10 + unit - 48
126+
elseif unit ~= 32 then
127+
return nil, "invalid data: " .. str_char(unit)
128+
end
129+
130+
if scale ~= nil then
131+
if scale > step then
132+
return nil, "unexpected unit: " .. str_char(unit)
133+
end
134+
135+
step = scale
136+
size = size + scale * size_in_unit
137+
size_in_unit = 0
138+
end
139+
end
140+
141+
if size_in_unit > 0 then
142+
if step == 1 then
143+
return nil, "specific unit conflicts with the default unit second"
144+
end
145+
146+
size = size + size_in_unit
147+
end
148+
149+
if with_ms then
150+
size = size / 1000
151+
end
152+
153+
return size
154+
end
155+
156+
68157
return _M

t/core/config_util.t

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#
2+
# Licensed to the Apache Software Foundation (ASF) under one or more
3+
# contributor license agreements. See the NOTICE file distributed with
4+
# this work for additional information regarding copyright ownership.
5+
# The ASF licenses this file to You under the Apache License, Version 2.0
6+
# (the "License"); you may not use this file except in compliance with
7+
# the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
use t::APISIX 'no_plan';
18+
19+
repeat_each(1);
20+
no_long_string();
21+
no_root_location();
22+
23+
add_block_preprocessor(sub {
24+
my ($block) = @_;
25+
26+
if (!$block->request) {
27+
$block->set_value("request", "GET /t");
28+
}
29+
30+
if (!$block->no_error_log && !$block->error_log) {
31+
$block->set_value("no_error_log", "[error]\n[alert]");
32+
}
33+
});
34+
35+
run_tests;
36+
37+
__DATA__
38+
39+
=== TEST 1: parse_time_unit
40+
--- config
41+
location /t {
42+
content_by_lua_block {
43+
local parse_time_unit = require("apisix.core.config_util").parse_time_unit
44+
for _, case in ipairs({
45+
{exp = 1, input = "1"},
46+
{exp = 1, input = "1s"},
47+
{exp = 60, input = "60s"},
48+
{exp = 1.1, input = "1s100ms"},
49+
{exp = 10.001, input = "10s1ms"},
50+
{exp = 3600, input = "60m"},
51+
{exp = 3600.11, input = "60m110ms"},
52+
{exp = 3710, input = "1h110"},
53+
{exp = 5400, input = "1h 30m"},
54+
{exp = 34822861.001, input = "1y1M1w1d1h1m1s1ms"},
55+
}) do
56+
assert(case.exp == parse_time_unit(case.input),
57+
string.format("input %s, got %s", case.input,
58+
parse_time_unit(case.input)))
59+
end
60+
61+
for _, case in ipairs({
62+
{exp = "invalid data: -", input = "-1"},
63+
{exp = "unexpected unit: h", input = "1m1h"},
64+
{exp = "invalid data: ", input = ""},
65+
{exp = "specific unit conflicts with the default unit second", input = "1s1"},
66+
}) do
67+
local _, err = parse_time_unit(case.input)
68+
assert(case.exp == err,
69+
string.format("input %s, got %s", case.input, err))
70+
end
71+
}
72+
}

t/node/upstream-keepalive-pool.t

+60
Original file line numberDiff line numberDiff line change
@@ -215,3 +215,63 @@ lua balancer: keepalive create pool, crc32: \S+, size: 1
215215
lua balancer: keepalive no free connection, cpool: \S+
216216
lua balancer: keepalive saving connection \S+, cpool: \S+, connections: 1
217217
$/
218+
219+
220+
221+
=== TEST 6: set upstream without keepalive_pool
222+
--- config
223+
location /t {
224+
content_by_lua_block {
225+
local t = require("lib.test_admin").test
226+
local code, body = t('/apisix/admin/upstreams/1',
227+
ngx.HTTP_PUT,
228+
[[{
229+
"type": "roundrobin",
230+
"nodes": {
231+
"127.0.0.1:1980": 1
232+
}
233+
}]]
234+
)
235+
if code >= 300 then
236+
ngx.status = code
237+
ngx.print(body)
238+
return
239+
end
240+
}
241+
}
242+
243+
244+
245+
=== TEST 7: should not override default value
246+
--- config
247+
location /t {
248+
content_by_lua_block {
249+
local http = require "resty.http"
250+
local uri = "http://127.0.0.1:" .. ngx.var.server_port
251+
.. "/hello"
252+
for i = 1, 3 do
253+
local httpc = http.new()
254+
local res, err = httpc:request_uri(uri)
255+
if not res then
256+
ngx.say(err)
257+
return
258+
end
259+
ngx.print(res.body)
260+
end
261+
}
262+
}
263+
--- response_body
264+
hello world
265+
hello world
266+
hello world
267+
--- grep_error_log eval
268+
qr/lua balancer: keepalive .*/
269+
--- grep_error_log_out eval
270+
qr/^lua balancer: keepalive create pool, crc32: \S+, size: 320
271+
lua balancer: keepalive no free connection, cpool: \S+
272+
lua balancer: keepalive saving connection \S+, cpool: \S+, connections: 1
273+
lua balancer: keepalive reusing connection \S+, requests: 1, cpool: \S+
274+
lua balancer: keepalive saving connection \S+, cpool: \S+, connections: 1
275+
lua balancer: keepalive reusing connection \S+, requests: 2, cpool: \S+
276+
lua balancer: keepalive saving connection \S+, cpool: \S+, connections: 1
277+
$/

0 commit comments

Comments
 (0)