diff options
author | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2018-02-13 14:23:46 +0000 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2018-02-13 14:23:46 +0000 |
commit | a186808ef7f9b42f820d14276d2f2a5fd44cad09 (patch) | |
tree | 89e1ed0bef226001a4ba4fd43852d3b03f44e8cd | |
parent | 0e668b86594901d34e4b31a6016c2aaabcd46f34 (diff) | |
download | rspamd-a186808ef7f9b42f820d14276d2f2a5fd44cad09.tar.gz rspamd-a186808ef7f9b42f820d14276d2f2a5fd44cad09.zip |
[Feature] Add routine to convert old style stats to a new one
-rw-r--r-- | lualib/stat_tools.lua | 104 |
1 files changed, 100 insertions, 4 deletions
diff --git a/lualib/stat_tools.lua b/lualib/stat_tools.lua index 6caf8a483..0f1ce5d59 100644 --- a/lualib/stat_tools.lua +++ b/lualib/stat_tools.lua @@ -24,9 +24,106 @@ local exports = {} local N = "stats_tools" -- Performs synchronous conversation of redis schema -local function convert_bayes_schema(cfg, redis_params, key) +local function convert_bayes_schema(redis_params, symbol_spam, symbol_ham, expire) + -- Old schema is the following one: + -- Keys are named <symbol>[<user>] + -- Elements are placed within hash: + -- BAYES_SPAM -> {<id1>: <num_hits>, <id2>: <num_hits> ...} + -- In new schema it is changed to a more extensible schema: + -- Keys are named RS[<user>]_<id> -> {'H': <ham_hits>, 'S': <spam_hits>} + -- So we can expire individual records, measure most popular elements by zranges, + -- add new fields, such as tokens etc + + local res,conn,_ = lua_redis.redis_connect_sync(redis_params, true) + + if not res then + logger.errx("cannot connect to redis server") + return false + end + + -- KEYS[1]: key to check (e.g. 'BAYES_SPAM') + -- KEYS[2]: hash key ('S' or 'H') + -- KEYS[3]: expire + local lua_script = [[ +local keys = redis.call('SMEMBERS', KEYS[1]..'_keys') +local nconverted = 0 + +for _,k in ipairs(keys) do + local elts = redis.call('HGETALL', k) + + for k,v in pairs(elts) do + local neutral_prefix = string.gsub(k, KEYS[1], 'RS') + local nkey = string.format('%s_%s', neutral_prefix, k) + redis.call('HSET', nkey, KEYS[2], v) + if KEYS[4] and tonumber(KEYS[3]) ~= 0 then + redis.call('EXPIRE', nkey, KEYS[3]) + end + nconverted = nconverted + 1 + end +end + +return nconverted +]] + + conn:add_cmd('EVAL', {lua_script, '3', symbol_spam, 'S', tostring(expire)}) + local ret, res = conn:exec() + + if not ret then + logger.errx('error converting symbol %s', symbol_spam) + return false + else + logger.infox('converted %s elements from symbol %s', res, symbol_spam) + end + + conn:add_cmd('EVAL', {lua_script, '3', symbol_ham, 'H', tostring(expire)}) + ret, res = conn:exec() + + if not ret then + logger.errx('error converting symbol %s', symbol_ham) + return false + else + logger.infox('converted %s elements from symbol %s', res, symbol_ham) + end + + -- We can now convert metadata: set + learned + version + -- KEYS[1]: key to check (e.g. 'BAYES_SPAM') + -- KEYS[2]: learn key (e.g. 'learns_spam' or 'learns_ham') + lua_script = [[ +local keys = redis.call('SMEMBERS', KEYS[1]..'_keys') + +for _,k in ipairs(keys) do + local learns = redis.call('HGET', k, 'learns') + local neutral_prefix = string.gsub(k, KEYS[1], 'RS') + + redis.call('HSET', neutral_prefix, KEYS[2], learns) + redis.call('SADD', KEYS[1]..'_keys', neutral_prefix) + redis.call('SREM', KEYS[1]..'_keys', k) + redis.call('DEL', k) + redis.call('SET', KEYS[1]..'_version', '2') end +]] + + conn:add_cmd('EVAL', {lua_script, '2', symbol_spam, 'learns_spam'}) + local ret, _ = conn:exec() + + if not ret then + logger.errx('error converting metadata for symbol %s', symbol_spam) + return false + end + + conn:add_cmd('EVAL', {lua_script, '2', symbol_ham, 'learns_ham'}) + local ret, _ = conn:exec() + + if not ret then + logger.errx('error converting metadata for symbol %s', symbol_ham) + return false + end + + return true +end + +exports.convert_bayes_schema = convert_bayes_schema -- It now accepts both ham and spam databases -- parameters: @@ -59,8 +156,7 @@ local function convert_sqlite_to_redis(redis_params, return false end - - local res,conn,addr = lua_redis.redis_connect_sync(redis_params, true) + local res,conn,_ = lua_redis.redis_connect_sync(redis_params, true) if not res then logger.errx("cannot connect to redis server") @@ -71,7 +167,7 @@ local function convert_sqlite_to_redis(redis_params, -- Do a more complicated cleanup -- execute a lua script that cleans up data local script = [[ -local members = redis.call('SMEMBERS', KEYS[1]) +local members = redis.call('SMEMBERS', KEYS[1]..'_keys') for _,prefix in ipairs(members) do local keys = redis.call('KEYS', prefix..'*') |