2019-05-03 08:17:27 +10:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2018-03-15 17:10:45 -04:00
|
|
|
module CachedCounting
|
|
|
|
extend ActiveSupport::Concern
|
|
|
|
|
2022-02-15 16:55:21 +00:00
|
|
|
EXPIRE_CACHE_AFTER = 4.days.to_i
|
|
|
|
|
|
|
|
LUA_INCR_AND_EXPIRE = DiscourseRedis::EvalHelper.new <<~LUA
|
|
|
|
local result = redis.call("INCR", KEYS[1])
|
|
|
|
|
|
|
|
if result == 1 then
|
|
|
|
redis.call("EXPIRE", KEYS[1], ARGV[1])
|
|
|
|
end
|
|
|
|
|
|
|
|
return result
|
|
|
|
LUA
|
|
|
|
|
2018-03-15 17:10:45 -04:00
|
|
|
included do
|
|
|
|
class << self
|
|
|
|
attr_accessor :autoflush, :autoflush_seconds, :last_flush
|
|
|
|
end
|
|
|
|
|
|
|
|
# auto flush if backlog is larger than this
|
|
|
|
self.autoflush = 2000
|
|
|
|
|
|
|
|
# auto flush if older than this
|
|
|
|
self.autoflush_seconds = 5.minutes
|
|
|
|
|
|
|
|
self.last_flush = Time.now.utc
|
|
|
|
end
|
|
|
|
|
|
|
|
class_methods do
|
|
|
|
def perform_increment!(key, opts = nil)
|
2022-02-15 16:55:21 +00:00
|
|
|
val = DiscourseRedis.ignore_readonly do
|
|
|
|
LUA_INCR_AND_EXPIRE.eval(
|
|
|
|
Discourse.redis.without_namespace,
|
|
|
|
[Discourse.redis.namespace_key(key)],
|
|
|
|
[EXPIRE_CACHE_AFTER]
|
|
|
|
).to_i
|
|
|
|
end
|
2018-03-15 17:10:45 -04:00
|
|
|
|
2022-02-15 16:55:21 +00:00
|
|
|
# readonly mode it is going to be nil, skip
|
|
|
|
return if val.nil?
|
2018-03-15 17:10:45 -04:00
|
|
|
|
|
|
|
autoflush = (opts && opts[:autoflush]) || self.autoflush
|
|
|
|
if autoflush > 0 && val >= autoflush
|
|
|
|
write_cache!
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
if (Time.now.utc - last_flush).to_i > autoflush_seconds
|
|
|
|
write_cache!
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def write_cache!(date = nil)
|
|
|
|
raise NotImplementedError
|
|
|
|
end
|
|
|
|
|
|
|
|
# this may seem a bit fancy but in so it allows
|
|
|
|
# for concurrent calls without double counting
|
|
|
|
def get_and_reset(key)
|
2022-02-15 16:55:21 +00:00
|
|
|
Discourse.redis.set(key, '0', ex: EXPIRE_CACHE_AFTER, get: true).to_i
|
2018-03-15 17:10:45 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def request_id(query_params, retries = 0)
|
2019-10-21 11:32:27 +01:00
|
|
|
id = where(query_params).pluck_first(:id)
|
2018-03-15 17:10:45 -04:00
|
|
|
id ||= create!(query_params.merge(count: 0)).id
|
|
|
|
rescue # primary key violation
|
|
|
|
if retries == 0
|
|
|
|
request_id(query_params, 1)
|
|
|
|
else
|
|
|
|
raise
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|