Blame db/cache.py

Ivan Mahonin b838e2
Ivan Mahonin b838e2
import threading
Ivan Mahonin b838e2
Ivan Mahonin b838e2
Ivan Mahonin b838e2
class CacheItem:
Ivan Mahonin b838e2
  def __init__(self, owner, key, value):
Ivan Mahonin b838e2
    self.owner = owner
Ivan Mahonin b838e2
    self.key = key
Ivan Mahonin b838e2
    self.value = value
Ivan Mahonin b838e2
    
Ivan Mahonin b838e2
    self.prev = None
Ivan Mahonin b838e2
    self.next = self.owner.first
Ivan Mahonin b838e2
    if self.next:
Ivan Mahonin b838e2
      self.next.prev = self
Ivan Mahonin b838e2
    else:
Ivan Mahonin b838e2
      self.owner.last = self
Ivan Mahonin b838e2
    self.owner.first = self
Ivan Mahonin b838e2
  
Ivan Mahonin b838e2
  def touch(self):
Ivan Mahonin b838e2
    if not self.prev:
Ivan Mahonin b838e2
      return
Ivan Mahonin b838e2
    self.prev.next = self.next
Ivan Mahonin b838e2
    if self.next:
Ivan Mahonin b838e2
      self.next.prev = self.prev
Ivan Mahonin b838e2
    else:
Ivan Mahonin b838e2
      self.owner.last = self.prev
Ivan Mahonin b838e2
    return self.value
Ivan Mahonin b838e2
Ivan Mahonin b838e2
  def detouch(self):
Ivan Mahonin b838e2
    if self.prev:
Ivan Mahonin b838e2
      self.prev.next = self.next
Ivan Mahonin b838e2
    else:
Ivan Mahonin b838e2
      self.owner.first = self.next
Ivan Mahonin b838e2
    if self.next:
Ivan Mahonin b838e2
      self.next.prev = self.prev
Ivan Mahonin b838e2
    else:
Ivan Mahonin b838e2
      self.owner.last = self.prev
Ivan Mahonin b838e2
Ivan Mahonin b838e2
Ivan Mahonin b838e2
class CacheTable:
Ivan Mahonin b838e2
  def __init__(self, maxcount):
Ivan Mahonin b838e2
    self.items = dict()
Ivan Mahonin b838e2
    self.first = None
Ivan Mahonin b838e2
    self.last = None
Ivan Mahonin b838e2
    self.maxcount = maxcount
Ivan Mahonin b838e2
    
Ivan Mahonin b838e2
  def get(self, key):
Ivan Mahonin b838e2
    item = self.items.get(key, None)
Ivan Mahonin b838e2
    return item.touch() if item else None
Ivan Mahonin b838e2
  
Ivan Mahonin b838e2
  def set(self, key, value):
Ivan Mahonin b838e2
    item = self.items.get(key, None)
Ivan Mahonin b838e2
    if item:
Ivan Mahonin b838e2
      item.value = value
Ivan Mahonin b838e2
      item.touch()
Ivan Mahonin b838e2
      return
Ivan Mahonin b838e2
    self.items[key] = CacheItem(self, key, value)
Ivan Mahonin b838e2
    while len(self.items) > self.maxcount:
Ivan Mahonin b838e2
      del self.items[self.last.key]
Ivan Mahonin b838e2
      self.last.detouch()
Ivan Mahonin b838e2
Ivan Mahonin b838e2
  def unset(self, key):
Ivan Mahonin b838e2
    item = self.items.get(key, None)
Ivan Mahonin b838e2
    if item:
Ivan Mahonin b838e2
      del self.items[key]
Ivan Mahonin b838e2
      item.detouch()
Ivan Mahonin b838e2
    
Ivan Mahonin b838e2
  def clear(self):
Ivan Mahonin b838e2
    self.first = None
Ivan Mahonin b838e2
    self.last = None
Ivan Mahonin b838e2
    # remove cyclic references to help GC
Ivan Mahonin b838e2
    for v in self.items.values():
Ivan Mahonin b838e2
      v.prev = None
Ivan Mahonin b838e2
      v.next = None
Ivan Mahonin b838e2
    self.items.clear()
Ivan Mahonin b838e2
    self.count = 0
Ivan Mahonin b838e2
Ivan Mahonin b838e2
Ivan Mahonin b838e2
class Cache:
Ivan Mahonin b838e2
  def __init__(self, server):
Ivan Mahonin b838e2
    self.server = server
Ivan Mahonin b838e2
    self.lock = threading.Lock()
Ivan Mahonin b838e2
    self.tables = dict()
Ivan Mahonin b838e2
    self.maxcount = self.server.config['db']['cache']['maxcount']
Ivan Mahonin b838e2
  
Ivan Mahonin b838e2
  def create_connection(self, connection):
Ivan Mahonin b838e2
    return CacheConnection(self, connection)
Ivan Mahonin b838e2
  
Ivan Mahonin b838e2
  def clear(self):
Ivan Mahonin b838e2
    with self.lock:
Ivan Mahonin b838e2
      for t in self.tables.values():
Ivan Mahonin b838e2
        t.clear()
Ivan Mahonin b838e2
      self.tables.clear()
Ivan Mahonin b838e2
  
Ivan Mahonin b838e2
  def build_select(self, connection, table, fields):
Ivan Mahonin b838e2
    assert(type(fields) is dict)
Ivan Mahonin b838e2
    where = list()
Ivan Mahonin b838e2
    args = list()
Ivan Mahonin b838e2
    for k, v in fields.items():
Ivan Mahonin b838e2
      assert(type(k) is str)
Ivan Mahonin b838e2
      if type(v) is int:
Ivan Mahonin b838e2
        where.append('%F=%d')
Ivan Mahonin b838e2
        args.append(k)
Ivan Mahonin b838e2
        args.append(v)
Ivan Mahonin b838e2
      elif type(v) is str:
Ivan Mahonin b838e2
        where.append('%F=%s')
Ivan Mahonin b838e2
        args.append(k)
Ivan Mahonin b838e2
        args.append(v)
Ivan Mahonin b838e2
      else:
Ivan Mahonin b838e2
        assert(False)
Ivan Mahonin b838e2
    where = ' AND '.join(where) if where else '1'
Ivan Mahonin b838e2
    return connection.parse('SELECT * FROM %T WHERE ' + where, table, *args)
Ivan Mahonin b838e2
    
Ivan Mahonin b838e2
  def select(self, connection, table, fields):
Ivan Mahonin b838e2
    assert(type(table) is str)
Ivan Mahonin b838e2
    sql = self.build_select(connection, table, fields)
Ivan Mahonin b838e2
    
Ivan Mahonin e5b0ac
    rows = None
Ivan Mahonin e5b0ac
Ivan Mahonin e5b0ac
    # cache does not support transactions and disabled
Ivan Mahonin e5b0ac
    #
Ivan Mahonin e5b0ac
    #with self.lock:
Ivan Mahonin e5b0ac
    #  tbl = self.tables.get(table)
Ivan Mahonin e5b0ac
    #  if not tbl:
Ivan Mahonin e5b0ac
    #    self.tables[table] = tbl = CacheTable(self.maxcount)
Ivan Mahonin e5b0ac
    #  rows = tbl.get(sql)
Ivan Mahonin b838e2
     
Ivan Mahonin b838e2
    if rows is None:
Ivan Mahonin b838e2
      rows = connection.query_dict(sql)
Ivan Mahonin b838e2
      assert(type(rows) is list)
Ivan Mahonin e5b0ac
      #with self.lock:
Ivan Mahonin e5b0ac
      #  tbl = self.tables.get(table)
Ivan Mahonin e5b0ac
      #  if not tbl:
Ivan Mahonin e5b0ac
      #    self.tables[table] = tbl = CacheTable(self.maxcount)
Ivan Mahonin e5b0ac
      #  tbl.set(sql, rows)
Ivan Mahonin e5b0ac
    
Ivan Mahonin b838e2
    return rows
Ivan Mahonin b838e2
Ivan Mahonin b838e2
  def reset(self, connection, table, fields = None):
Ivan Mahonin b838e2
    assert(type(table) is str)
Ivan Mahonin e5b0ac
    assert(not connection.readonly)
Ivan Mahonin e5b0ac
    return
Ivan Mahonin b838e2
    sql = None
Ivan Mahonin b838e2
    if not fields is None:
Ivan Mahonin b838e2
      sql = self.build_select(connection, table, fields)
Ivan Mahonin b838e2
    with self.lock:
Ivan Mahonin b838e2
      tbl = self.tables.get(table)
Ivan Mahonin b838e2
      if tbl:
Ivan Mahonin b838e2
        if sql is None:
Ivan Mahonin b838e2
          tbl.clear()
Ivan Mahonin b838e2
        else:
Ivan Mahonin b838e2
          tbl.unset(sql)
Ivan Mahonin b838e2
    
Ivan Mahonin b838e2
Ivan Mahonin b838e2
class CacheConnection:
Ivan Mahonin b838e2
  def __init__(self, cache, connection):
Ivan Mahonin b838e2
    self.cache = cache
Ivan Mahonin b838e2
    self.connection = connection
Ivan Mahonin b838e2
Ivan Mahonin b838e2
  def clear(self):
Ivan Mahonin b838e2
    self.cache.clear()
Ivan Mahonin b838e2
Ivan Mahonin b838e2
  def select(self, table, fields):
Ivan Mahonin b838e2
    return self.cache.select(self.connection, table, fields)
Ivan Mahonin b838e2
Ivan Mahonin b838e2
  def reset(self, table, fields = None):
Ivan Mahonin b838e2
    self.cache.reset(self.connection, table, fields)
Ivan Mahonin b838e2
Ivan Mahonin b838e2
  def row(self, table, id):
Ivan Mahonin b838e2
    rows = self.select(table, {'id': id})
Ivan Mahonin b838e2
    if len(rows) == 0:
Ivan Mahonin b838e2
      return None
Ivan Mahonin b838e2
    assert(len(rows) == 1)
Ivan Mahonin b838e2
    return rows[0]
Ivan Mahonin b838e2
Ivan Mahonin b838e2
  def reset_row(self, table, id):
Ivan Mahonin b838e2
    self.reset(table, {'id': id})
Ivan Mahonin b838e2