This is an automated email from the ASF dual-hosted git repository.
spacewander pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new d2d826e fix(log-rotate): avoid creating multiple timers (#2596)
d2d826e is described below
commit d2d826e6ab39980a757bdeb48b76960765509c7c
Author: 罗泽轩 <[email protected]>
AuthorDate: Mon Nov 9 09:09:33 2020 +0800
fix(log-rotate): avoid creating multiple timers (#2596)
---
apisix/init.lua | 2 +
apisix/plugin.lua | 24 +++++++++-
apisix/plugins/example-plugin.lua | 10 +++++
apisix/plugins/log-rotate.lua | 21 +++------
apisix/timers.lua | 88 ++++++++++++++++++++++++++++++++++++
t/plugin/log-rotate.t | 94 +++++++++++++++++++++++++++++++++++----
6 files changed, 214 insertions(+), 25 deletions(-)
diff --git a/apisix/init.lua b/apisix/init.lua
index b2a795f..05a708f 100644
--- a/apisix/init.lua
+++ b/apisix/init.lua
@@ -98,6 +98,8 @@ function _M.http_init_worker()
load_balancer = require("apisix.balancer").run
require("apisix.admin.init").init_worker()
+ require("apisix.timers").init_worker()
+
router.http_init_worker()
require("apisix.http.service").init_worker()
plugin.init_worker()
diff --git a/apisix/plugin.lua b/apisix/plugin.lua
index 5936288..ae1aedd 100644
--- a/apisix/plugin.lua
+++ b/apisix/plugin.lua
@@ -56,12 +56,26 @@ local function sort_plugin(l, r)
end
-local function load_plugin(name, plugins_list, is_stream_plugin)
+local function unload_plugin(name, is_stream_plugin)
local pkg_name = "apisix.plugins." .. name
if is_stream_plugin then
pkg_name = "apisix.stream.plugins." .. name
end
+
+ local old_plugin = pkg_loaded[pkg_name]
+ if old_plugin and type(old_plugin.destory) == "function" then
+ old_plugin.destory()
+ end
+
pkg_loaded[pkg_name] = nil
+end
+
+
+local function load_plugin(name, plugins_list, is_stream_plugin)
+ local pkg_name = "apisix.plugins." .. name
+ if is_stream_plugin then
+ pkg_name = "apisix.stream.plugins." .. name
+ end
local ok, plugin = pcall(require, pkg_name)
if not ok then
@@ -115,6 +129,10 @@ local function load(plugin_names)
core.log.warn("new plugins: ", core.json.delay_encode(processed))
+ for name in pairs(local_plugins_hash) do
+ unload_plugin(name)
+ end
+
core.table.clear(local_plugins)
core.table.clear(local_plugins_hash)
@@ -159,6 +177,10 @@ local function load_stream(plugin_names)
core.log.warn("new plugins: ", core.json.delay_encode(processed))
+ for name in pairs(stream_local_plugins_hash) do
+ unload_plugin(name, true)
+ end
+
core.table.clear(stream_local_plugins)
core.table.clear(stream_local_plugins_hash)
diff --git a/apisix/plugins/example-plugin.lua
b/apisix/plugins/example-plugin.lua
index 9944929..a590f43 100644
--- a/apisix/plugins/example-plugin.lua
+++ b/apisix/plugins/example-plugin.lua
@@ -61,6 +61,16 @@ function _M.check_schema(conf)
end
+function _M.init()
+ -- call this function when plugin is loaded
+end
+
+
+function _M.destory()
+ -- call this function when plugin is unloaded
+end
+
+
function _M.rewrite(conf, ctx)
core.log.warn("plugin rewrite phase, conf: ", core.json.encode(conf))
-- core.log.warn(" ctx: ", core.json.encode(ctx, true))
diff --git a/apisix/plugins/log-rotate.lua b/apisix/plugins/log-rotate.lua
index 6287b55..de2266c 100644
--- a/apisix/plugins/log-rotate.lua
+++ b/apisix/plugins/log-rotate.lua
@@ -16,6 +16,7 @@
--
local core = require("apisix.core")
+local timers = require("apisix.timers")
local process = require("ngx.process")
local signal = require("resty.signal")
local ngx = ngx
@@ -27,7 +28,6 @@ local string = string
local local_conf
-local timer
local plugin_name = "log-rotate"
local INTERVAL = 60 * 60 -- rotate interval (unit: second)
local MAX_KEPT = 24 * 7 -- max number of log files will be kept
@@ -205,23 +205,12 @@ end
function _M.init()
- core.log.info("enter log-rotate plugin, process type: ", process.type())
-
- if process.type() ~= "privileged agent" and process.type() ~= "single" then
- return
- end
+ timers.register_timer("plugin#log-rotate", rotate, true)
+end
- if timer then
- return
- end
- local err
- timer, err = core.timer.new("logrotate", rotate, {check_interval = 0.5})
- if not timer then
- core.log.error("failed to create timer log rotate: ", err)
- else
- core.log.notice("succeed to create timer: log rotate")
- end
+function _M.destory()
+ timers.unregister_timer("plugin#log-rotate", true)
end
diff --git a/apisix/timers.lua b/apisix/timers.lua
new file mode 100644
index 0000000..51b0271
--- /dev/null
+++ b/apisix/timers.lua
@@ -0,0 +1,88 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements. See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local core = require("apisix.core")
+local process = require("ngx.process")
+local pairs = pairs
+local unpack = unpack
+local thread_spawn = ngx.thread.spawn
+local thread_wait = ngx.thread.wait
+
+
+local timers = {}
+
+
+local _M = {}
+
+
+local function background_timer()
+ local threads = {}
+ for name, timer in pairs(timers) do
+ core.log.info("run timer[", name, "]")
+
+ local th, err = thread_spawn(timer)
+ if not th then
+ core.log.error("failed to spawn thread for timer [", name, "]: ",
err)
+ goto continue
+ end
+
+ core.table.insert(threads, th)
+
+::continue::
+ end
+
+ local ok, err = thread_wait(unpack(threads))
+ if not ok then
+ core.log.error("failed to wait threads: ", err)
+ end
+end
+
+
+local function is_privileged()
+ return process.type() == "privileged agent" or process.type() == "single"
+end
+
+
+function _M.init_worker()
+ local timer, err = core.timer.new("background", background_timer,
{check_interval = 0.5})
+ if not timer then
+ core.log.error("failed to create background timer: ", err)
+ return
+ end
+
+ core.log.notice("succeed to create background timer")
+end
+
+
+function _M.register_timer(name, f, privileged)
+ if privileged and not is_privileged() then
+ return
+ end
+
+ timers[name] = f
+end
+
+
+function _M.unregister_timer(name, privileged)
+ if privileged and not is_privileged() then
+ return
+ end
+
+ timers[name] = nil
+end
+
+
+return _M
diff --git a/t/plugin/log-rotate.t b/t/plugin/log-rotate.t
index 3cf08cb..29b66ef 100644
--- a/t/plugin/log-rotate.t
+++ b/t/plugin/log-rotate.t
@@ -28,6 +28,7 @@ add_block_preprocessor(sub {
my $user_yaml_config = <<_EOC_;
apisix:
node_listen: 1984
+ admin_key: null
plugins: # plugin list
- log-rotate
@@ -39,6 +40,7 @@ plugin_attr:
_EOC_
$block->set_value("yaml_config", $user_yaml_config);
+ $block->set_value("request", "GET /t");
});
run_tests;
@@ -64,21 +66,19 @@ __DATA__
local content = f:read("*all")
f:close()
local index = string.find(content, "start xxxxxx")
- if index then
+ if index then
has_split_error_file = true
end
end
end
-
- if not has_split_error_file or not has_split_error_file then
+
+ if not has_split_error_file or not has_split_error_file then
ngx.status = 500
else
ngx.status = 200
- end
+ end
}
}
---- request
-GET /t
--- error_code eval
[200]
--- no_error_log
@@ -95,11 +95,89 @@ GET /t
ngx.say("done")
}
}
---- request
-GET /t
--- response_body
done
--- no_error_log
[error]
--- error_log
start xxxxxx
+
+
+
+=== TEST 3: fix: ensure only one timer is running
+--- config
+ location /t {
+ content_by_lua_block {
+ ngx.sleep(0.5)
+ local t = require("lib.test_admin").test
+ local code, _, org_body = t('/apisix/admin/plugins/reload',
+ ngx.HTTP_PUT)
+
+ ngx.status = code
+ ngx.say(org_body)
+
+ ngx.sleep(1)
+
+ local lfs = require("lfs")
+ for file_name in lfs.dir(ngx.config.prefix() .. "/logs/") do
+ if string.match(file_name, "__error.log$") then
+ local f = assert(io.open(ngx.config.prefix() .. "/logs/"
.. file_name, "r"))
+ local content = f:read("*all")
+ f:close()
+ local counter = 0
+ ngx.re.gsub(content, [=[run timer\[plugin#log-rotate\]]=],
function()
+ counter = counter + 1
+ return ""
+ end)
+
+ if counter ~= 1 then
+ ngx.say("not a single rotater run at the same time: ",
file_name)
+ end
+ end
+ end
+ }
+ }
+--- response_body
+done
+--- no_error_log
+[error]
+
+
+
+=== TEST 4: disable log-rotate via hot reload
+--- config
+ location /t {
+ content_by_lua_block {
+ local data = [[
+apisix:
+ node_listen: 1984
+ admin_key: null
+plugins:
+ - prometheus
+ ]]
+ require("lib.test_admin").set_config_yaml(data)
+ local t = require("lib.test_admin").test
+ local code, _, org_body = t('/apisix/admin/plugins/reload',
+ ngx.HTTP_PUT)
+
+ ngx.status = code
+ ngx.say(org_body)
+
+ ngx.sleep(1.5)
+
+ local n_split_error_file = 0
+ local lfs = require("lfs")
+ for file_name in lfs.dir(ngx.config.prefix() .. "/logs/") do
+ if string.match(file_name, "__error.log$") then
+ n_split_error_file = n_split_error_file + 1
+ end
+ end
+
+ ngx.say(n_split_error_file)
+ }
+ }
+--- response_body
+done
+1
+--- no_error_log
+[error]