You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

fuzzy_convert.lua 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. local sqlite3 = require "rspamd_sqlite3"
  2. local redis = require "rspamd_redis"
  3. local util = require "rspamd_util"
  4. local function connect_redis(server, password, db)
  5. local ret
  6. local conn, err = redis.connect_sync({
  7. host = server,
  8. })
  9. if not conn then
  10. return nil, 'Cannot connect: ' .. err
  11. end
  12. if password then
  13. ret = conn:add_cmd('AUTH', {password})
  14. if not ret then
  15. return nil, 'Cannot queue command'
  16. end
  17. end
  18. if db then
  19. ret = conn:add_cmd('SELECT', {db})
  20. if not ret then
  21. return nil, 'Cannot queue command'
  22. end
  23. end
  24. return conn, nil
  25. end
  26. local function send_digests(digests, redis_host, redis_password, redis_db)
  27. local conn, err = connect_redis(redis_host, redis_password, redis_db)
  28. if err then
  29. print(err)
  30. return false
  31. end
  32. local ret
  33. for _, v in ipairs(digests) do
  34. ret = conn:add_cmd('HMSET', {
  35. 'fuzzy' .. v[1],
  36. 'F', v[2],
  37. 'V', v[3],
  38. })
  39. if not ret then
  40. print('Cannot batch command')
  41. return false
  42. end
  43. ret = conn:add_cmd('EXPIRE', {
  44. 'fuzzy' .. v[1],
  45. tostring(v[4]),
  46. })
  47. if not ret then
  48. print('Cannot batch command')
  49. return false
  50. end
  51. end
  52. ret, err = conn:exec()
  53. if not ret then
  54. print('Cannot execute batched commands: ' .. err)
  55. return false
  56. end
  57. return true
  58. end
  59. local function send_shingles(shingles, redis_host, redis_password, redis_db)
  60. local conn, err = connect_redis(redis_host, redis_password, redis_db)
  61. if err then
  62. print("Redis error: " .. err)
  63. return false
  64. end
  65. local ret
  66. for _, v in ipairs(shingles) do
  67. ret = conn:add_cmd('SET', {
  68. 'fuzzy_' .. v[2] .. '_' .. v[1],
  69. v[4],
  70. })
  71. if not ret then
  72. print('Cannot batch SET command: ' .. err)
  73. return false
  74. end
  75. ret = conn:add_cmd('EXPIRE', {
  76. 'fuzzy_' .. v[2] .. '_' .. v[1],
  77. tostring(v[3]),
  78. })
  79. if not ret then
  80. print('Cannot batch command')
  81. return false
  82. end
  83. end
  84. ret, err = conn:exec()
  85. if not ret then
  86. print('Cannot execute batched commands: ' .. err)
  87. return false
  88. end
  89. return true
  90. end
  91. local function update_counters(total, redis_host, redis_password, redis_db)
  92. local conn, err = connect_redis(redis_host, redis_password, redis_db)
  93. if err then
  94. print(err)
  95. return false
  96. end
  97. local ret
  98. ret = conn:add_cmd('SET', {
  99. 'fuzzylocal',
  100. total,
  101. })
  102. if not ret then
  103. print('Cannot batch command')
  104. return false
  105. end
  106. ret = conn:add_cmd('SET', {
  107. 'fuzzy_count',
  108. total,
  109. })
  110. if not ret then
  111. print('Cannot batch command')
  112. return false
  113. end
  114. ret, err = conn:exec()
  115. if not ret then
  116. print('Cannot execute batched commands: ' .. err)
  117. return false
  118. end
  119. return true
  120. end
  121. return function (_, res)
  122. local db = sqlite3.open(res['source_db'])
  123. local shingles = {}
  124. local digests = {}
  125. local num_batch_digests = 0
  126. local num_batch_shingles = 0
  127. local total_digests = 0
  128. local total_shingles = 0
  129. local lim_batch = 1000 -- Update each 1000 entries
  130. local redis_password = res['redis_password']
  131. local redis_db = nil
  132. if res['redis_db'] then
  133. redis_db = tostring(res['redis_db'])
  134. end
  135. if not db then
  136. print('Cannot open source db: ' .. res['source_db'])
  137. return
  138. end
  139. local now = util.get_time()
  140. for row in db:rows('SELECT id, flag, digest, value, time FROM digests') do
  141. local expire_in = math.floor(now - row.time + res['expiry'])
  142. if expire_in >= 1 then
  143. table.insert(digests, {row.digest, row.flag, row.value, expire_in})
  144. num_batch_digests = num_batch_digests + 1
  145. total_digests = total_digests + 1
  146. for srow in db:rows('SELECT value, number FROM shingles WHERE digest_id = ' .. row.id) do
  147. table.insert(shingles, {srow.value, srow.number, expire_in, row.digest})
  148. total_shingles = total_shingles + 1
  149. num_batch_shingles = num_batch_shingles + 1
  150. end
  151. end
  152. if num_batch_digests >= lim_batch then
  153. if not send_digests(digests, res['redis_host'], redis_password, redis_db) then
  154. return
  155. end
  156. num_batch_digests = 0
  157. digests = {}
  158. end
  159. if num_batch_shingles >= lim_batch then
  160. if not send_shingles(shingles, res['redis_host'], redis_password, redis_db) then
  161. return
  162. end
  163. num_batch_shingles = 0
  164. shingles = {}
  165. end
  166. end
  167. if digests[1] then
  168. if not send_digests(digests, res['redis_host'], redis_password, redis_db) then
  169. return
  170. end
  171. end
  172. if shingles[1] then
  173. if not send_shingles(shingles, res['redis_host'], redis_password, redis_db) then
  174. return
  175. end
  176. end
  177. local message = string.format(
  178. 'Migrated %d digests and %d shingles',
  179. total_digests, total_shingles
  180. )
  181. if not update_counters(total_digests, res['redis_host'], redis_password, redis_db) then
  182. message = message .. ' but failed to update counters'
  183. end
  184. print(message)
  185. end