import logging
import random
try:
import redis
except ImportError:
logging.error('"redis" module is required to access Redis server.')
[docs]
class RedisClientError(Exception):
pass
[docs]
class RedisClient(object):
client = None
[docs]
@classmethod
def get_client(cls, params):
if cls.client is None:
cls.client = cls(params)
return cls.client
def __init__(self, connection_params):
self._mapdb = None
self.connection_params = connection_params
@property
def mapdb(self):
if self._mapdb is None:
self._mapdb = redis.StrictRedis(db=0, **self.connection_params)
return self._mapdb
[docs]
def get_db(self, db_name=None):
"""
Return a redict client instance from a database name or
database number (if db_name is an integer)
"""
self.check()
try:
db_num = int(db_name)
except ValueError:
db_num = self.mapdb.get(db_name)
if not db_num:
db_num = self.pick_db()
self.mapdb.set(db_name, int(db_num))
return redis.StrictRedis(db=int(db_num), **self.connection_params)
[docs]
def pick_db(self):
"""
Return a database number, preferably not used (db doesn't exist).
If no database available (all are used), will be one and flush it...
"""
db_max_num = int(self.mapdb.config_get("databases")["databases"] or 16)
# -1: we always keep db=0 (meta db)
avail = dict(zip(range(1, db_max_num), [True] * (db_max_num - 1)))
for info in self.mapdb.info("keyspace"):
if not info.startswith("db"):
continue
num = int(info.replace("db", ""))
if num == 0:
continue
avail.pop(num)
if not avail:
db_num = random.randint(1, db_max_num - 1)
else:
db_num = random.choice(list(avail.keys()))
return db_num
[docs]
def check(self):
if not self.mapdb.get("__META__") == b"0":
raise RedisClientError("Can't find database metadata, you may want to use initialize()")
[docs]
def initialize(self, deep=False):
"""
Careful: this may delete data.
Prepare Redis instance to work with biothings hub:
- database 0: this db is used to store a mapping between
database index and database name (so a database can be accessed
by name). This method will flush this db and prepare it.
- any other databases will be flushed if deep is True, making the redis
server fully dedicated to
"""
if deep:
self.mapdb.flushall()
self.mapdb.flushdb()
self.mapdb.set("__META__", 0)
self.check()