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