Timeseries

redis-py supports RedisTimeSeries which is a time-series-database module for Redis.

This example shows how to handle timeseries data with redis-py.

Health check

[1]:
import redis

r = redis.Redis(decode_responses=True)
ts = r.ts()

r.ping()
[1]:
True

Simple example

Create a timeseries

[2]:
ts.create("ts_key")
[2]:
True

Add samples to the timeseries

We can either set the timestamp with an UNIX timestamp in milliseconds or use * to set the timestamp based en server’s clock.

[3]:
ts.add("ts_key", 1657265437756, 1)
ts.add("ts_key", "1657265437757", 2)
ts.add("ts_key", "*", 3)
[3]:
1657272304448

Get the last sample

[4]:
ts.get("ts_key")
[4]:
(1657272304448, 3.0)

Get samples between two timestamps

The minimum and maximum possible timestamps can be expressed with respectfully - and +.

[5]:
ts.range("ts_key", "-", "+")
[5]:
[(1657265437756, 1.0), (1657265437757, 2.0), (1657272304448, 3.0)]
[6]:
ts.range("ts_key", 1657265437756, 1657265437757)
[6]:
[(1657265437756, 1.0), (1657265437757, 2.0)]

Delete samples between two timestamps

[7]:
print("Before deletion: ", ts.range("ts_key", "-", "+"))
ts.delete("ts_key", 1657265437756, 1657265437757)
print("After deletion:  ", ts.range("ts_key", "-", "+"))
Before deletion:  [(1657265437756, 1.0), (1657265437757, 2.0), (1657272304448, 3.0)]
After deletion:   [(1657272304448, 3.0)]

Multiple timeseries with labels

[8]:
ts.create("ts_key1")
ts.create("ts_key2", labels={"label1": 1, "label2": 2})
[8]:
True

Add samples to multiple timeseries

[9]:
ts.madd([("ts_key1", "*", 1), ("ts_key2", "*", 2)])
[9]:
[1657272306147, 1657272306147]

Add samples with labels

[10]:
ts.add("ts_key2", "*", 2,  labels={"label1": 1, "label2": 2})
ts.add("ts_key2", "*", 2,  labels={"label1": 3, "label2": 4})
[10]:
1657272306457

Get the last sample matching specific label

Get the last sample that matches “label1=1”, see Redis documentation to see the posible filter values.

[11]:
ts.mget(["label1=1"])
[11]:
[{'ts_key2': [{}, 1657272306457, 2.0]}]

Get also the label-value pairs of the sample:

[12]:
ts.mget(["label1=1"], with_labels=True)
[12]:
[{'ts_key2': [{'label1': '1', 'label2': '2'}, 1657272306457, 2.0]}]

Retention period

You can specify a retention period when creating timeseries objects or when adding a sample timeseries object. Once the retention period has elapsed, the sample is removed from the timeseries.

[13]:
retention_time = 1000
ts.create("ts_key_ret", retention_msecs=retention_time)
[13]:
True
[14]:
import time
# this will be deleted in 1000 milliseconds
ts.add("ts_key_ret", "*", 1, retention_msecs=retention_time)
print("Base timeseries:                     ", ts.range("ts_key_ret", "-", "+"))
# sleeping for 1000 milliseconds (1 second)
time.sleep(1)
print("Timeseries after 1000 milliseconds:  ", ts.range("ts_key_ret", "-", "+"))
Base timeseries:                      [(1657272307670, 1.0)]
Timeseries after 1000 milliseconds:   [(1657272307670, 1.0)]

The two lists are the same, this is because the oldest values are deleted when a new sample is added.

[15]:
ts.add("ts_key_ret", "*", 10)
[15]:
1657272308849
[16]:
ts.range("ts_key_ret", "-", "+")
[16]:
[(1657272308849, 10.0)]

Here the first sample has been deleted.

Specify duplicate policies

By default, the policy for duplicates timestamp keys is set to “BLOCK”, we cannot create two samples with the same timestamp:

[17]:
ts.add("ts_key", 123456789, 1)
try:
    ts.add("ts_key", 123456789, 2)
except Exception as err:
    print(err)
TSDB: Error at upsert, update is not supported when DUPLICATE_POLICY is set to BLOCK mode

You can change this default behaviour using duplicate_policy parameter, for instance:

[18]:
# using policy "LAST", we keep the last added sample
ts.add("ts_key", 123456789, 2, duplicate_policy="LAST")
ts.range("ts_key", "-", "+")
[18]:
[(123456789, 2.0), (1657272304448, 3.0)]

For more informations about duplicate policies, see Redis documentation.

Using Redis TSDB to keep track of a value

[19]:
ts.add("ts_key_incr", "*", 0)
[19]:
1657272310241

Increment the value:

[20]:
for _ in range(10):
    ts.incrby("ts_key_incr", 1)
    # sleeping a bit so the timestamp are not duplicates
    time.sleep(0.01)
[21]:
ts.range("ts_key_incr", "-", "+")
[21]:
[(1657272310241, 0.0),
 (1657272310533, 1.0),
 (1657272310545, 2.0),
 (1657272310556, 3.0),
 (1657272310567, 4.0),
 (1657272310578, 5.0),
 (1657272310589, 6.0),
 (1657272310600, 7.0),
 (1657272310611, 8.0),
 (1657272310622, 9.0),
 (1657272310632, 10.0)]