fix
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
import redis
|
||||
from redis._parsers.helpers import bool_ok
|
||||
|
||||
from ..helpers import get_protocol_version, parse_to_list
|
||||
from .commands import (
|
||||
ALTER_CMD,
|
||||
CREATE_CMD,
|
||||
CREATERULE_CMD,
|
||||
DEL_CMD,
|
||||
DELETERULE_CMD,
|
||||
GET_CMD,
|
||||
INFO_CMD,
|
||||
MGET_CMD,
|
||||
MRANGE_CMD,
|
||||
MREVRANGE_CMD,
|
||||
QUERYINDEX_CMD,
|
||||
RANGE_CMD,
|
||||
REVRANGE_CMD,
|
||||
TimeSeriesCommands,
|
||||
)
|
||||
from .info import TSInfo
|
||||
from .utils import parse_get, parse_m_get, parse_m_range, parse_range
|
||||
|
||||
|
||||
class TimeSeries(TimeSeriesCommands):
|
||||
"""
|
||||
This class subclasses redis-py's `Redis` and implements RedisTimeSeries's
|
||||
commands (prefixed with "ts").
|
||||
The client allows to interact with RedisTimeSeries and use all of it's
|
||||
functionality.
|
||||
"""
|
||||
|
||||
def __init__(self, client=None, **kwargs):
|
||||
"""Create a new RedisTimeSeries client."""
|
||||
# Set the module commands' callbacks
|
||||
self._MODULE_CALLBACKS = {
|
||||
ALTER_CMD: bool_ok,
|
||||
CREATE_CMD: bool_ok,
|
||||
CREATERULE_CMD: bool_ok,
|
||||
DELETERULE_CMD: bool_ok,
|
||||
}
|
||||
|
||||
_RESP2_MODULE_CALLBACKS = {
|
||||
DEL_CMD: int,
|
||||
GET_CMD: parse_get,
|
||||
INFO_CMD: TSInfo,
|
||||
MGET_CMD: parse_m_get,
|
||||
MRANGE_CMD: parse_m_range,
|
||||
MREVRANGE_CMD: parse_m_range,
|
||||
RANGE_CMD: parse_range,
|
||||
REVRANGE_CMD: parse_range,
|
||||
QUERYINDEX_CMD: parse_to_list,
|
||||
}
|
||||
_RESP3_MODULE_CALLBACKS = {}
|
||||
|
||||
self.client = client
|
||||
self.execute_command = client.execute_command
|
||||
|
||||
if get_protocol_version(self.client) in ["3", 3]:
|
||||
self._MODULE_CALLBACKS.update(_RESP3_MODULE_CALLBACKS)
|
||||
else:
|
||||
self._MODULE_CALLBACKS.update(_RESP2_MODULE_CALLBACKS)
|
||||
|
||||
for k, v in self._MODULE_CALLBACKS.items():
|
||||
self.client.set_response_callback(k, v)
|
||||
|
||||
def pipeline(self, transaction=True, shard_hint=None):
|
||||
"""Creates a pipeline for the TimeSeries module, that can be used
|
||||
for executing only TimeSeries commands and core commands.
|
||||
|
||||
Usage example:
|
||||
|
||||
r = redis.Redis()
|
||||
pipe = r.ts().pipeline()
|
||||
for i in range(100):
|
||||
pipeline.add("with_pipeline", i, 1.1 * i)
|
||||
pipeline.execute()
|
||||
|
||||
"""
|
||||
if isinstance(self.client, redis.RedisCluster):
|
||||
p = ClusterPipeline(
|
||||
nodes_manager=self.client.nodes_manager,
|
||||
commands_parser=self.client.commands_parser,
|
||||
startup_nodes=self.client.nodes_manager.startup_nodes,
|
||||
result_callbacks=self.client.result_callbacks,
|
||||
cluster_response_callbacks=self.client.cluster_response_callbacks,
|
||||
cluster_error_retry_attempts=self.client.cluster_error_retry_attempts,
|
||||
read_from_replicas=self.client.read_from_replicas,
|
||||
reinitialize_steps=self.client.reinitialize_steps,
|
||||
lock=self.client._lock,
|
||||
)
|
||||
|
||||
else:
|
||||
p = Pipeline(
|
||||
connection_pool=self.client.connection_pool,
|
||||
response_callbacks=self._MODULE_CALLBACKS,
|
||||
transaction=transaction,
|
||||
shard_hint=shard_hint,
|
||||
)
|
||||
return p
|
||||
|
||||
|
||||
class ClusterPipeline(TimeSeriesCommands, redis.cluster.ClusterPipeline):
|
||||
"""Cluster pipeline for the module."""
|
||||
|
||||
|
||||
class Pipeline(TimeSeriesCommands, redis.client.Pipeline):
|
||||
"""Pipeline for the module."""
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,91 @@
|
||||
from ..helpers import nativestr
|
||||
from .utils import list_to_dict
|
||||
|
||||
|
||||
class TSInfo:
|
||||
"""
|
||||
Hold information and statistics on the time-series.
|
||||
Can be created using ``tsinfo`` command
|
||||
https://redis.io/docs/latest/commands/ts.info/
|
||||
"""
|
||||
|
||||
rules = []
|
||||
labels = []
|
||||
sourceKey = None
|
||||
chunk_count = None
|
||||
memory_usage = None
|
||||
total_samples = None
|
||||
retention_msecs = None
|
||||
last_time_stamp = None
|
||||
first_time_stamp = None
|
||||
|
||||
max_samples_per_chunk = None
|
||||
chunk_size = None
|
||||
duplicate_policy = None
|
||||
|
||||
def __init__(self, args):
|
||||
"""
|
||||
Hold information and statistics on the time-series.
|
||||
|
||||
The supported params that can be passed as args:
|
||||
|
||||
rules:
|
||||
A list of compaction rules of the time series.
|
||||
sourceKey:
|
||||
Key name for source time series in case the current series
|
||||
is a target of a rule.
|
||||
chunkCount:
|
||||
Number of Memory Chunks used for the time series.
|
||||
memoryUsage:
|
||||
Total number of bytes allocated for the time series.
|
||||
totalSamples:
|
||||
Total number of samples in the time series.
|
||||
labels:
|
||||
A list of label-value pairs that represent the metadata
|
||||
labels of the time series.
|
||||
retentionTime:
|
||||
Retention time, in milliseconds, for the time series.
|
||||
lastTimestamp:
|
||||
Last timestamp present in the time series.
|
||||
firstTimestamp:
|
||||
First timestamp present in the time series.
|
||||
maxSamplesPerChunk:
|
||||
Deprecated.
|
||||
chunkSize:
|
||||
Amount of memory, in bytes, allocated for data.
|
||||
duplicatePolicy:
|
||||
Policy that will define handling of duplicate samples.
|
||||
|
||||
Can read more about on
|
||||
https://redis.io/docs/latest/develop/data-types/timeseries/configuration/#duplicate_policy
|
||||
"""
|
||||
response = dict(zip(map(nativestr, args[::2]), args[1::2]))
|
||||
self.rules = response.get("rules")
|
||||
self.source_key = response.get("sourceKey")
|
||||
self.chunk_count = response.get("chunkCount")
|
||||
self.memory_usage = response.get("memoryUsage")
|
||||
self.total_samples = response.get("totalSamples")
|
||||
self.labels = list_to_dict(response.get("labels"))
|
||||
self.retention_msecs = response.get("retentionTime")
|
||||
self.last_timestamp = response.get("lastTimestamp")
|
||||
self.first_timestamp = response.get("firstTimestamp")
|
||||
if "maxSamplesPerChunk" in response:
|
||||
self.max_samples_per_chunk = response["maxSamplesPerChunk"]
|
||||
self.chunk_size = (
|
||||
self.max_samples_per_chunk * 16
|
||||
) # backward compatible changes
|
||||
if "chunkSize" in response:
|
||||
self.chunk_size = response["chunkSize"]
|
||||
if "duplicatePolicy" in response:
|
||||
self.duplicate_policy = response["duplicatePolicy"]
|
||||
if isinstance(self.duplicate_policy, bytes):
|
||||
self.duplicate_policy = self.duplicate_policy.decode()
|
||||
|
||||
def get(self, item):
|
||||
try:
|
||||
return self.__getitem__(item)
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
def __getitem__(self, item):
|
||||
return getattr(self, item)
|
||||
@@ -0,0 +1,44 @@
|
||||
from ..helpers import nativestr
|
||||
|
||||
|
||||
def list_to_dict(aList):
|
||||
return {nativestr(aList[i][0]): nativestr(aList[i][1]) for i in range(len(aList))}
|
||||
|
||||
|
||||
def parse_range(response, **kwargs):
|
||||
"""Parse range response. Used by TS.RANGE and TS.REVRANGE."""
|
||||
return [tuple((r[0], float(r[1]))) for r in response]
|
||||
|
||||
|
||||
def parse_m_range(response):
|
||||
"""Parse multi range response. Used by TS.MRANGE and TS.MREVRANGE."""
|
||||
res = []
|
||||
for item in response:
|
||||
res.append({nativestr(item[0]): [list_to_dict(item[1]), parse_range(item[2])]})
|
||||
return sorted(res, key=lambda d: list(d.keys()))
|
||||
|
||||
|
||||
def parse_get(response):
|
||||
"""Parse get response. Used by TS.GET."""
|
||||
if not response:
|
||||
return None
|
||||
return int(response[0]), float(response[1])
|
||||
|
||||
|
||||
def parse_m_get(response):
|
||||
"""Parse multi get response. Used by TS.MGET."""
|
||||
res = []
|
||||
for item in response:
|
||||
if not item[2]:
|
||||
res.append({nativestr(item[0]): [list_to_dict(item[1]), None, None]})
|
||||
else:
|
||||
res.append(
|
||||
{
|
||||
nativestr(item[0]): [
|
||||
list_to_dict(item[1]),
|
||||
int(item[2][0]),
|
||||
float(item[2][1]),
|
||||
]
|
||||
}
|
||||
)
|
||||
return sorted(res, key=lambda d: list(d.keys()))
|
||||
Reference in New Issue
Block a user