|
|
- 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
|