Redis store¶
This page covers the store. For the Redis broker transport, see Redis transport.
RedisStore keeps task states, results and scheduler claims in Redis, so
producers, workers and schedulers in different processes or on different
machines share them.
Install¶
Import it from its own module. Without the extra, this import raises an
ImportError that tells you to install it:
The example needs a Redis server at redis://localhost:6379/0, so the
documentation tests do not run it:
"""A store on Redis, shared by every process that uses the same prefix."""
import asyncio
from asynk import App, Worker
from asynk.store.redis import RedisStore
from asynk.transport.memory import memory_transport
producer, consumer = memory_transport()
app = App(
"orders",
producer=producer,
consumer=consumer,
store=RedisStore("redis://localhost:6379/0", prefix="asynk:orders:"),
)
@app.task()
async def add(a: int, b: int) -> int:
return a + b
async def main() -> None:
worker = Worker(app)
run = asyncio.create_task(worker.run())
handle = await add.delay(1, b=2)
print("result:", await handle.result(timeout=5))
worker.request_shutdown()
await run
if __name__ == "__main__":
asyncio.run(main())
Configuration¶
RedisStore(url=None, *, client=None, ttl=86400.0, prefix="asynk:", max_poll_interval=1.0)
urlorclient: give exactly one. Withurl, the store builds the client and closes it onstop(). Withclient, aredis.asyncio.Redisyou built, you own it and close it yourself.ttl: seconds a key lives. Every write resets it.prefix: text before every key. Give each app that shares a Redis its own, for example"asynk:billing:".max_poll_interval: the longest pause, in seconds, between the checksresult()makes while it waits.
A client built from url uses a 5 second socket timeout and a 5 second
connect timeout. Query parameters in the URL override them
(redis://redis:6379/0?socket_timeout=10). For anything else, such as TLS
settings or retries, pass your own client.
How it maps to Redis¶
| Store method | Redis command |
|---|---|
set(key, value) |
SET prefix+key value PX <ttl in milliseconds> NX |
set(key, value, overwrite=True) |
SET prefix+key value PX <ttl in milliseconds> |
get(key) |
GET prefix+key |
delete(key) |
DEL prefix+key |
wait(key, timeout) |
GET repeated with a growing pause |
The keys are asynk:state:<id>, asynk:result:<id> and
asynk:claim:<task>:<time>, with your prefix in place of asynk:. Scheduler
claims use SET NX, which Redis runs atomically, so several schedulers can
share one store and exactly one publishes each slot.
Pitfalls¶
waitpolls.result()can lag by up tomax_poll_intervalafter the worker records the result.start()does not contact Redis. A wrong URL or a Redis that is down shows up as logged store errors, not as a failed startup. The worker and scheduler carry on without the store, as described in Results and the store.- A hung Redis slows every task and every
delay(): each store call waits up tosocket_timeout(5 seconds by default). A task pays that for its running state, its terminal state and its result write, anddelay()pays it once for the pending state. - The scheduler guarantee that exactly one scheduler publishes each slot holds only while Redis answers. When the claim fails, every scheduler publishes; see Scheduled tasks.
TaskHandle.state()andresult()raise the client'sredis.exceptions.ConnectionErrororTimeoutErrorwhen Redis is down. Readers see the error; workers and the scheduler carry on without the store.- A client you pass in without socket timeouts lets a hung Redis stall a claim or a record.
- A client you pass in must use
decode_responses=False: values are bytes. WithTrue,getraises aTypeError. - Redis Cluster clients (
RedisCluster) are not supported, andwaitdoes not use pub/sub wake-ups. - A producer-only process runs no worker or scheduler, so call
app.store.stop()when it is done to close the connection pool; see Store lifecycle. - Records last only as long as Redis keeps them: its persistence settings and eviction policy apply.