mirror of
https://github.com/rszamszur/fastapi-mvc-template.git
synced 2021-11-08 01:34:05 +03:00
274 lines
8.2 KiB
Python
274 lines
8.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Redis client class utility."""
|
|
import logging
|
|
|
|
import aioredis
|
|
import aioredis.sentinel
|
|
from aioredis.exceptions import RedisError
|
|
from fastapi_mvc_template.app.config.redis import (
|
|
REDIS_HOST,
|
|
REDIS_PORT,
|
|
REDIS_USERNAME,
|
|
REDIS_PASSWORD,
|
|
REDIS_USE_SENTINEL
|
|
)
|
|
|
|
|
|
class RedisClient(object):
|
|
"""Redis client utility.
|
|
|
|
Utility class for handling Redis database connection and operations.
|
|
|
|
Attributes:
|
|
redis_client (aioredis.Redis, optional): Redis client object instance.
|
|
log (logging.Logger): Logging handler for this class.
|
|
base_redis_init_kwargs (dict): Common kwargs regardless other Redis
|
|
configuration
|
|
connection_kwargs (dict, optional): Extra kwargs for Redis object init.
|
|
|
|
"""
|
|
|
|
redis_client: aioredis.Redis = None
|
|
log: logging.Logger = logging.getLogger(__name__)
|
|
base_redis_init_kwargs: dict = {
|
|
"encoding": "utf-8",
|
|
"port": REDIS_PORT
|
|
}
|
|
connection_kwargs: dict = {}
|
|
|
|
@classmethod
|
|
def open_redis_client(cls):
|
|
"""Create Redis client session object instance.
|
|
|
|
Based on configuration create either Redis client or Redis Sentinel.
|
|
|
|
Returns:
|
|
aioredis.Redis: Redis object instance.
|
|
|
|
"""
|
|
if cls.redis_client is None:
|
|
cls.log.debug("Initialize Redis client.")
|
|
if REDIS_USERNAME and REDIS_PASSWORD:
|
|
cls.connection_kwargs = {
|
|
"username": REDIS_USERNAME,
|
|
"password": REDIS_PASSWORD,
|
|
}
|
|
|
|
if REDIS_USE_SENTINEL:
|
|
sentinel = aioredis.sentinel.Sentinel(
|
|
[(REDIS_HOST, REDIS_PORT)],
|
|
sentinel_kwargs=cls.connection_kwargs
|
|
)
|
|
cls.redis_client = sentinel.master_for("mymaster")
|
|
else:
|
|
cls.base_redis_init_kwargs.update(cls.connection_kwargs)
|
|
cls.redis_client = aioredis.from_url(
|
|
"redis://{0:s}".format(REDIS_HOST),
|
|
**cls.base_redis_init_kwargs,
|
|
)
|
|
|
|
return cls.redis_client
|
|
|
|
@classmethod
|
|
async def close_redis_client(cls):
|
|
"""Close Redis client."""
|
|
if cls.redis_client:
|
|
cls.log.debug("Closing Redis client")
|
|
await cls.redis_client.close()
|
|
|
|
@classmethod
|
|
async def ping(cls):
|
|
"""Execute Redis PING command.
|
|
|
|
Ping the Redis server.
|
|
|
|
Returns:
|
|
response: Boolean, whether Redis client could ping Redis server.
|
|
|
|
Raises:
|
|
aioredis.RedisError: If Redis client failed while executing command.
|
|
|
|
"""
|
|
# Note: Not sure if this shouldn't be deep copy instead?
|
|
redis_client = cls.redis_client
|
|
|
|
cls.log.debug("Preform Redis PING command")
|
|
try:
|
|
return await redis_client.ping()
|
|
except RedisError as ex:
|
|
cls.log.exception(
|
|
"Redis PING command finished with exception",
|
|
exc_info=(type(ex), ex, ex.__traceback__)
|
|
)
|
|
raise ex
|
|
|
|
@classmethod
|
|
async def set(cls, key, value):
|
|
"""Execute Redis SET command.
|
|
|
|
Set key to hold the string value. If key already holds a value, it is
|
|
overwritten, regardless of its type.
|
|
|
|
Args:
|
|
key (str): Redis db key.
|
|
value (str): Value to be set.
|
|
|
|
Returns:
|
|
response: Redis SET command response, for more info
|
|
look: https://redis.io/commands/set#return-value
|
|
|
|
Raises:
|
|
aioredis.RedisError: If Redis client failed while executing command.
|
|
|
|
"""
|
|
redis_client = cls.redis_client
|
|
|
|
cls.log.debug(
|
|
"Preform Redis SET command, key: {}, value: {}".format(key, value)
|
|
)
|
|
try:
|
|
await redis_client.set(key, value)
|
|
except RedisError as ex:
|
|
cls.log.exception(
|
|
"Redis SET command finished with exception",
|
|
exc_info=(type(ex), ex, ex.__traceback__)
|
|
)
|
|
raise ex
|
|
|
|
@classmethod
|
|
async def rpush(cls, key, value):
|
|
"""Execute Redis RPUSH command.
|
|
|
|
Insert all the specified values at the tail of the list stored at key.
|
|
If key does not exist, it is created as empty list before performing
|
|
the push operation. When key holds a value that is not a list, an
|
|
error is returned.
|
|
|
|
Args:
|
|
key (str): Redis db key.
|
|
value (str, list): Single or multiple values to append.
|
|
|
|
Returns:
|
|
response: Length of the list after the push operation.
|
|
|
|
Raises:
|
|
aioredis.RedisError: If Redis client failed while executing command.
|
|
|
|
"""
|
|
redis_client = cls.redis_client
|
|
|
|
cls.log.debug(
|
|
"Preform Redis RPUSH command, key: {}, value: {}".format(key, value)
|
|
)
|
|
try:
|
|
await redis_client.rpush(key, value)
|
|
except RedisError as ex:
|
|
cls.log.exception(
|
|
"Redis RPUSH command finished with exception",
|
|
exc_info=(type(ex), ex, ex.__traceback__)
|
|
)
|
|
raise ex
|
|
|
|
@classmethod
|
|
async def exists(cls, key):
|
|
"""Execute Redis EXISTS command.
|
|
|
|
Returns if key exists.
|
|
|
|
Args:
|
|
key (str): Redis db key.
|
|
|
|
Returns:
|
|
response: Boolean whether key exists in Redis db.
|
|
|
|
Raises:
|
|
aioredis.RedisError: If Redis client failed while executing command.
|
|
|
|
"""
|
|
redis_client = cls.redis_client
|
|
|
|
cls.log.debug(
|
|
"Preform Redis EXISTS command, key: {}, exists".format(key)
|
|
)
|
|
try:
|
|
return await redis_client.exists(key)
|
|
except RedisError as ex:
|
|
cls.log.exception(
|
|
"Redis EXISTS command finished with exception",
|
|
exc_info=(type(ex), ex, ex.__traceback__)
|
|
)
|
|
raise ex
|
|
|
|
@classmethod
|
|
async def get(cls, key):
|
|
"""Execute Redis GET command.
|
|
|
|
Get the value of key. If the key does not exist the special value None
|
|
is returned. An error is returned if the value stored at key is not a
|
|
string, because GET only handles string values.
|
|
|
|
Args:
|
|
key (str): Redis db key.
|
|
|
|
Returns:
|
|
response: Value of key.
|
|
|
|
Raises:
|
|
aioredis.RedisError: If Redis client failed while executing command.
|
|
|
|
"""
|
|
redis_client = cls.redis_client
|
|
|
|
cls.log.debug(
|
|
"Preform Redis GET command, key: {}".format(key)
|
|
)
|
|
try:
|
|
return await redis_client.get(key)
|
|
except RedisError as ex:
|
|
cls.log.exception(
|
|
"Redis GET command finished with exception",
|
|
exc_info=(type(ex), ex, ex.__traceback__)
|
|
)
|
|
raise ex
|
|
|
|
@classmethod
|
|
async def lrange(cls, key, start, end):
|
|
"""Execute Redis LRANGE command.
|
|
|
|
Returns the specified elements of the list stored at key. The offsets
|
|
start and stop are zero-based indexes, with 0 being the first element
|
|
of the list (the head of the list), 1 being the next element and so on.
|
|
These offsets can also be negative numbers indicating offsets starting
|
|
at the end of the list. For example, -1 is the last element of the
|
|
list, -2 the penultimate, and so on.
|
|
|
|
Args:
|
|
key (str): Redis db key.
|
|
start (int): Start offset value.
|
|
end (int): End offset value.
|
|
|
|
Returns:
|
|
response: Returns the specified elements of the list stored at key.
|
|
|
|
Raises:
|
|
aioredis.RedisError: If Redis client failed while executing command.
|
|
|
|
"""
|
|
redis_client = cls.redis_client
|
|
|
|
cls.log.debug(
|
|
"Preform Redis LRANGE command, key: {}, start: {}, end: {}".format(
|
|
key,
|
|
start,
|
|
end,
|
|
)
|
|
)
|
|
try:
|
|
return await redis_client.lrange(key, start, end)
|
|
except RedisError as ex:
|
|
cls.log.exception(
|
|
"Redis LRANGE command finished with exception",
|
|
exc_info=(type(ex), ex, ex.__traceback__)
|
|
)
|
|
raise ex
|