150 lines
3.9 KiB
Python
150 lines
3.9 KiB
Python
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
|
# MIT License. See license.txt
|
|
from __future__ import unicode_literals
|
|
|
|
import redis, frappe, re
|
|
import cPickle as pickle
|
|
from frappe.utils import cstr
|
|
|
|
class RedisWrapper(redis.Redis):
|
|
"""Redis client that will automatically prefix conf.db_name"""
|
|
def make_key(self, key, user=None):
|
|
if user:
|
|
if user == True:
|
|
user = frappe.session.user
|
|
|
|
key = "user:{0}:{1}".format(user, key)
|
|
|
|
return "{0}|{1}".format(frappe.conf.db_name, key).encode('utf-8')
|
|
|
|
def set_value(self, key, val, user=None):
|
|
"""Sets cache value."""
|
|
key = self.make_key(key, user)
|
|
frappe.local.cache[key] = val
|
|
try:
|
|
self.set(key, pickle.dumps(val))
|
|
except redis.exceptions.ConnectionError:
|
|
return None
|
|
|
|
def get_value(self, key, generator=None, user=None):
|
|
"""Returns cache value. If not found and generator function is
|
|
given, it will call the generator.
|
|
|
|
:param key: Cache key.
|
|
:param generator: Function to be called to generate a value if `None` is returned."""
|
|
original_key = key
|
|
key = self.make_key(key, user)
|
|
|
|
if key not in frappe.local.cache:
|
|
val = None
|
|
try:
|
|
val = self.get(key)
|
|
except redis.exceptions.ConnectionError:
|
|
pass
|
|
if val is not None:
|
|
val = pickle.loads(val)
|
|
if val is None and generator:
|
|
val = generator()
|
|
self.set_value(original_key, val, user=user)
|
|
else:
|
|
frappe.local.cache[key] = val
|
|
|
|
return frappe.local.cache.get(key)
|
|
|
|
def get_all(self, key):
|
|
ret = {}
|
|
for k in self.get_keys(key):
|
|
ret[key] = self.get_value(k)
|
|
|
|
return ret
|
|
|
|
def get_keys(self, key):
|
|
"""Return keys starting with `key`."""
|
|
try:
|
|
key = self.make_key(key + "*")
|
|
return self.keys(key)
|
|
|
|
except redis.exceptions.ConnectionError:
|
|
regex = re.compile(cstr(key).replace("|", "\|").replace("*", "[\w]*"))
|
|
return [k for k in frappe.local.cache.keys() if regex.match(k)]
|
|
|
|
def delete_keys(self, key):
|
|
"""Delete keys with wildcard `*`."""
|
|
try:
|
|
self.delete_value(self.get_keys(key), make_keys=False)
|
|
except redis.exceptions.ConnectionError:
|
|
pass
|
|
|
|
def delete_key(self, *args, **kwargs):
|
|
self.delete_value(*args, **kwargs)
|
|
|
|
def delete_value(self, keys, user=None, make_keys=True):
|
|
"""Delete value, list of values."""
|
|
if not isinstance(keys, (list, tuple)):
|
|
keys = (keys, )
|
|
|
|
for key in keys:
|
|
if make_keys:
|
|
key = self.make_key(key)
|
|
|
|
try:
|
|
self.delete(key)
|
|
except redis.exceptions.ConnectionError:
|
|
pass
|
|
|
|
if key in frappe.local.cache:
|
|
del frappe.local.cache[key]
|
|
|
|
def hset(self, name, key, value):
|
|
if not name in frappe.local.cache:
|
|
frappe.local.cache[name] = {}
|
|
frappe.local.cache[name][key] = value
|
|
try:
|
|
super(redis.Redis, self).hset(self.make_key(name), key, pickle.dumps(value))
|
|
except redis.exceptions.ConnectionError:
|
|
pass
|
|
|
|
def hget(self, name, key, generator=None):
|
|
if not name in frappe.local.cache:
|
|
frappe.local.cache[name] = {}
|
|
if key in frappe.local.cache[name]:
|
|
return frappe.local.cache[name][key]
|
|
|
|
value = None
|
|
try:
|
|
value = super(redis.Redis, self).hget(self.make_key(name), key)
|
|
except redis.exceptions.ConnectionError:
|
|
pass
|
|
|
|
if value:
|
|
value = pickle.loads(value)
|
|
frappe.local.cache[name][key] = value
|
|
elif generator:
|
|
value = generator()
|
|
try:
|
|
self.hset(name, key, value)
|
|
except redis.exceptions.ConnectionError:
|
|
pass
|
|
return value
|
|
|
|
def hdel(self, name, key):
|
|
if name in frappe.local.cache:
|
|
if key in frappe.local.cache[name]:
|
|
del frappe.local.cache[name][key]
|
|
try:
|
|
super(redis.Redis, self).hdel(self.make_key(name), key)
|
|
except redis.exceptions.ConnectionError:
|
|
pass
|
|
|
|
def hdel_keys(self, name_starts_with, key):
|
|
"""Delete hash names with wildcard `*` and key"""
|
|
for name in frappe.cache().get_keys(name_starts_with):
|
|
name = name.split("|", 1)[1]
|
|
self.hdel(name, key)
|
|
|
|
def hkeys(self, name):
|
|
try:
|
|
return super(redis.Redis, self).hkeys(self.make_key(name))
|
|
except redis.exceptions.ConnectionError:
|
|
return []
|
|
|