require "sqlite3"
|
|
require "../config.cr"
|
|
|
|
class CacheLRU
|
|
def initialize
|
|
@store = Hash(String, Tuple(Time, User)).new
|
|
@mutex = Mutex.new
|
|
@cache_limit = 5000
|
|
end
|
|
|
|
def try(pair) : User | Nil
|
|
@mutex.synchronize do
|
|
if @store.has_key?(pair)
|
|
return @store[pair][1]
|
|
else return nil
|
|
end
|
|
end
|
|
end
|
|
|
|
def push(pair, data)
|
|
@mutex.synchronize do
|
|
@store[pair] = Tuple.new(Time.now+Time::Span.new(1,0,0), data)
|
|
if(@store.size > @cache_limit)
|
|
clean
|
|
end
|
|
end
|
|
end
|
|
|
|
def clean
|
|
@mutex.synchronize do
|
|
curr = Time.now
|
|
@store.reject! do |k, v|
|
|
return v[0]>curr
|
|
end
|
|
end
|
|
end
|
|
|
|
def invalidate(pair)
|
|
@mutex.synchronize do
|
|
@store.reject! pair
|
|
end
|
|
end
|
|
end
|
|
|
|
class KVStore
|
|
property database : DB::Database
|
|
property st_fetch : DB::PoolPreparedStatement
|
|
property st_push : DB::PoolPreparedStatement
|
|
property cache : CacheLRU
|
|
|
|
def initialize
|
|
@database = DB.open(Statics.data_path)
|
|
|
|
@database.exec "create table if not exists kv (k TEXT PRIMARY KEY, v TEXT);"
|
|
|
|
@st_fetch = DB::PoolPreparedStatement.new @database, "select v from kv where k=?;"
|
|
@st_push = DB::PoolPreparedStatement.new @database, "insert into kv(k,v) values(?,?) on conflict(k) do update set v=?;"
|
|
@cache = CacheLRU.new
|
|
end
|
|
|
|
def fetch(key : String) : User | Nil
|
|
begin
|
|
f = @cache.try(key)
|
|
if(f.nil?)
|
|
v = @st_fetch.scalar(key)
|
|
u = User.from_json v.to_s
|
|
@cache.push(key, u)
|
|
if(v.nil?)
|
|
return nil
|
|
else
|
|
return u
|
|
end
|
|
else
|
|
return f
|
|
end
|
|
rescue ex
|
|
return nil
|
|
end
|
|
end
|
|
|
|
def fetch!(key : String) : User
|
|
fetch(key).not_nil!
|
|
end
|
|
|
|
def push(key : String, value : String)
|
|
@cache.invalidate key
|
|
@st_push.exec(key, value, value)
|
|
end
|
|
|
|
def transaction()
|
|
@database.transaction do |tx|
|
|
yield self
|
|
end
|
|
end
|
|
|
|
def self.access
|
|
@@instance ||= new
|
|
end
|
|
end
|