tilde.news/app/models/keystore.rb

78 lines
1.7 KiB
Ruby

# typed: false
class Keystore < ApplicationRecord
MAX_KEY_LENGTH = 50
self.primary_key = "key"
validates :key, presence: true, length: {maximum: MAX_KEY_LENGTH}
def self.get(key)
find_by(key: key)
end
def self.value_for(key)
where(key: key).limit(1).pick(:value)
end
def self.put(key, value)
validate_input_key(key)
Keystore.upsert({key: key, value: value}, returning: false)
true
end
def self.increment_value_for(key, amount = 1)
incremented_value_for(key, amount)
end
def self.incremented_value_for(key, amount = 1)
validate_input_key(key)
Keystore.transaction do
Keystore.upsert({key: key, value: amount}, on_duplicate: Arel.sql("value = value + 1"))
value_for(key)
end
end
def self.find_or_create_key_for_update(key, init = nil)
loop do
found = lock(true).find_by(key: key)
return found if found
begin
create! do |kv|
kv.key = key
kv.value = init
kv.save!
end
rescue ActiveRecord::RecordNotUnique
nil
end
end
end
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
# 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
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
end