summaryrefslogtreecommitdiffstats
path: root/src/plugins/lua/maps_stats.lua
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/lua/maps_stats.lua')
-rw-r--r--src/plugins/lua/maps_stats.lua133
1 files changed, 133 insertions, 0 deletions
diff --git a/src/plugins/lua/maps_stats.lua b/src/plugins/lua/maps_stats.lua
new file mode 100644
index 0000000..d418810
--- /dev/null
+++ b/src/plugins/lua/maps_stats.lua
@@ -0,0 +1,133 @@
+--[[
+Copyright (c) 2022, Vsevolod Stakhov <vsevolod@rspamd.com>
+
+Licensed 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.
+]]--
+
+if confighelp then
+ rspamd_config:add_example(nil, 'maps_stats',
+ "Stores maps statistics in Redis", [[
+maps_stats {
+ # one iteration step per 2 minutes
+ interval = 2m;
+ # how many elements to store in Redis
+ count = 1k;
+ # common prefix for elements
+ prefix = 'rm_';
+}
+]])
+end
+
+local redis_params
+local lua_util = require "lua_util"
+local rspamd_logger = require "rspamd_logger"
+local lua_redis = require "lua_redis"
+local N = "maps_stats"
+
+local settings = {
+ interval = 120, -- one iteration step per 2 minutes
+ count = 1000, -- how many elements to store in Redis
+ prefix = 'rm_', -- common prefix for elements
+}
+
+local function process_map(map, ev_base, _)
+ if map:get_nelts() > 0 and map:get_uri() ~= 'static' then
+ local key = settings.prefix .. map:get_uri()
+
+ local function redis_zrange_cb(err, data)
+ if err then
+ rspamd_logger.errx(rspamd_config, 'cannot delete extra elements in %s: %s',
+ key, err)
+ elseif data then
+ rspamd_logger.infox(rspamd_config, 'cleared %s elements from %s',
+ data, key)
+ end
+ end
+ local function redis_card_cb(err, data)
+ if err then
+ rspamd_logger.errx(rspamd_config, 'cannot get number of elements in %s: %s',
+ key, err)
+ elseif data then
+ if settings.count > 0 and tonumber(data) > settings.count then
+ lua_redis.rspamd_redis_make_request_taskless(ev_base,
+ rspamd_config,
+ redis_params, -- connect params
+ key, -- hash key
+ true, -- is write
+ redis_zrange_cb, --callback
+ 'ZREMRANGEBYRANK', -- command
+ { key, '0', tostring(-(settings.count) - 1) } -- arguments
+ )
+ end
+ end
+ end
+ local ret, conn, _ = lua_redis.rspamd_redis_make_request_taskless(ev_base,
+ rspamd_config,
+ redis_params, -- connect params
+ key, -- hash key
+ true, -- is write
+ redis_card_cb, --callback
+ 'ZCARD', -- command
+ { key } -- arguments
+ )
+
+ if ret and conn then
+ local stats = map:get_stats(true)
+ for k, s in pairs(stats) do
+ if s > 0 then
+ conn:add_cmd('ZINCRBY', { key, tostring(s), k })
+ end
+ end
+ end
+ end
+end
+
+if not lua_util.check_experimental(N) then
+ return
+end
+
+local opts = rspamd_config:get_all_opt(N)
+
+if opts then
+ for k, v in pairs(opts) do
+ settings[k] = v
+ end
+end
+
+redis_params = lua_redis.parse_redis_server(N, opts)
+-- XXX, this is a poor approach as not all maps are defined here...
+local tmaps = rspamd_config:get_maps()
+for _, m in ipairs(tmaps) do
+ if m:get_uri() ~= 'static' then
+ lua_redis.register_prefix(settings.prefix .. m:get_uri(), N,
+ 'Maps stats data', {
+ type = 'zlist',
+ persistent = true,
+ })
+ end
+end
+
+if redis_params then
+ rspamd_config:add_on_load(function(_, ev_base, worker)
+ local maps = rspamd_config:get_maps()
+
+ for _, m in ipairs(maps) do
+ rspamd_config:add_periodic(ev_base,
+ settings['interval'],
+ function()
+ process_map(m, ev_base, worker)
+ return true
+ end, true)
+ end
+ end)
+end \ No newline at end of file