mirror of
https://github.com/discourse/discourse.git
synced 2024-11-30 04:34:13 -06:00
68 lines
1.6 KiB
Ruby
68 lines
1.6 KiB
Ruby
|
module CachedCounting
|
||
|
extend ActiveSupport::Concern
|
||
|
|
||
|
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)
|
||
|
val = $redis.incr(key).to_i
|
||
|
|
||
|
# readonly mode it is going to be 0, skip
|
||
|
return if val == 0
|
||
|
|
||
|
# 3.days, see: https://github.com/rails/rails/issues/21296
|
||
|
$redis.expire(key, 259200)
|
||
|
|
||
|
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
|
||
|
|
||
|
GET_AND_RESET = <<~LUA
|
||
|
local val = redis.call('get', KEYS[1])
|
||
|
redis.call('set', KEYS[1], '0')
|
||
|
return val
|
||
|
LUA
|
||
|
|
||
|
# this may seem a bit fancy but in so it allows
|
||
|
# for concurrent calls without double counting
|
||
|
def get_and_reset(key)
|
||
|
namespaced_key = $redis.namespace_key(key)
|
||
|
$redis.without_namespace.eval(GET_AND_RESET, keys: [namespaced_key]).to_i
|
||
|
end
|
||
|
|
||
|
def request_id(query_params, retries = 0)
|
||
|
id = where(query_params).pluck(:id).first
|
||
|
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
|