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