2023-09-14 13:37:09 +00:00
|
|
|
# typed: false
|
|
|
|
|
2018-03-19 00:12:01 +00:00
|
|
|
class Keystore < ApplicationRecord
|
2020-02-05 15:02:13 +00:00
|
|
|
MAX_KEY_LENGTH = 50
|
|
|
|
|
2014-01-09 05:01:45 +00:00
|
|
|
self.primary_key = "key"
|
2012-06-17 01:15:46 +00:00
|
|
|
|
2020-02-05 15:02:13 +00:00
|
|
|
validates :key, presence: true, length: {maximum: MAX_KEY_LENGTH}
|
2012-06-17 01:15:46 +00:00
|
|
|
|
|
|
|
def self.get(key)
|
2022-03-19 13:15:20 +00:00
|
|
|
find_by(key: key)
|
2012-06-17 01:15:46 +00:00
|
|
|
end
|
|
|
|
|
2014-01-09 05:01:45 +00:00
|
|
|
def self.value_for(key)
|
2022-03-19 22:02:07 +00:00
|
|
|
where(key: key).limit(1).pluck(:value).first
|
2014-01-09 05:01:45 +00:00
|
|
|
end
|
|
|
|
|
2012-06-17 01:15:46 +00:00
|
|
|
def self.put(key, value)
|
2020-02-05 15:02:13 +00:00
|
|
|
validate_input_key(key)
|
2023-12-18 02:22:47 +00:00
|
|
|
Keystore.upsert({key: key, value: value}, returning: false)
|
2012-06-17 01:15:46 +00:00
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.increment_value_for(key, amount = 1)
|
|
|
|
incremented_value_for(key, amount)
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.incremented_value_for(key, amount = 1)
|
2020-02-05 15:02:13 +00:00
|
|
|
validate_input_key(key)
|
2012-10-08 17:45:15 +00:00
|
|
|
Keystore.transaction do
|
2023-12-18 02:22:47 +00:00
|
|
|
Keystore.upsert({key: key, value: amount}, on_duplicate: Arel.sql("value = value + 1"))
|
2014-01-09 05:01:45 +00:00
|
|
|
value_for(key)
|
2012-07-02 01:44:22 +00:00
|
|
|
end
|
2014-01-09 05:01:45 +00:00
|
|
|
end
|
2012-06-17 01:15:46 +00:00
|
|
|
|
2014-01-09 05:01:45 +00:00
|
|
|
def self.find_or_create_key_for_update(key, init = nil)
|
|
|
|
loop do
|
2018-03-19 20:55:42 +00:00
|
|
|
found = lock(true).find_by(key: key)
|
|
|
|
return found if found
|
2014-01-09 05:01:45 +00:00
|
|
|
|
|
|
|
begin
|
|
|
|
create! do |kv|
|
|
|
|
kv.key = key
|
|
|
|
kv.value = init
|
|
|
|
kv.save!
|
|
|
|
end
|
|
|
|
rescue ActiveRecord::RecordNotUnique
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
end
|
2012-06-17 01:15:46 +00:00
|
|
|
end
|
2013-02-14 00:50:51 +00:00
|
|
|
|
2022-03-19 13:15:20 +00:00
|
|
|
def self.decrement_value_for(key, amount = -1)
|
|
|
|
increment_value_for(key, amount)
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.decremented_value_for(key, amount = -1)
|
|
|
|
incremented_value_for(key, amount)
|
|
|
|
end
|
|
|
|
|
2019-04-21 16:47:13 +00:00
|
|
|
# deliberately no lock/transaction as TrafficHelper is on the hot path of every request
|
|
|
|
def self.readthrough_cache(key, &blk)
|
|
|
|
if (found = value_for(key))
|
|
|
|
found
|
|
|
|
else
|
|
|
|
value = yield blk
|
|
|
|
put(key, value)
|
|
|
|
value
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-02-05 15:02:13 +00:00
|
|
|
def self.validate_input_key(key)
|
|
|
|
exception = ActiveRecord::ValueTooLong.new("#{MAX_KEY_LENGTH}" \
|
|
|
|
" characters is the maximum allowed for key")
|
|
|
|
raise exception if key.length > MAX_KEY_LENGTH
|
|
|
|
end
|
2012-06-17 01:15:46 +00:00
|
|
|
end
|