Hey <@ULVA73B9P>, I have some redis keys building ...
# ask-marvin
s
Hey @Marvin, I have some redis keys building up. Can you explain what sort of usage patterns are likely to cause these to accumulate?
Copy code
1) "message: 151465 keys | TTL - min: 0s, max: 300s, avg: 115s (151465/151465 with TTL)"
2) "events: 1 keys | No TTL set"
3) "task-run-recorder: 24137 keys | TTL - min: 0s, max: 1800s, avg: 1006s (24137/24137 with TTL)"
4) "actions: 1 keys | No TTL set"
5) "triggers: 99586 keys | TTL - min: 0s, max: 1800s, avg: 883s (99586/99586 with TTL)"
m
thought for 188.5 seconds
Thanks for sharing the breakdown — this looks like a healthy mix of Prefect’s Redis-backed messaging and causal-ordering caches. Here’s what each prefix generally means in Prefect 3.x and what usage patterns make them accumulate. Quick map of your prefixes - message: … with TTL up to ~300s - What it is: a short-lived deduplication cache for published messages (events are typically published with deduplicate_by="id"). - Why it grows: high event publish rates will create one dedup key per unique message id within the dedup window. - Expected TTL: ~5 minutes (300s). Keys auto-expire. - events: 1 key | No TTL - What it is: the Redis stream that carries all published events. - Why it’s 1 key: Redis streams are single keys that hold many entries internally; the key itself has no TTL. Old entries are trimmed by the server (XTRIM). - How to watch growth: check stream length (e.g., XLEN) rather than key count. - actions: 1 key | No TTL - What it is: the Redis stream used for automation actions. - Same behavior as events (single stream key, trimmed over time). - task-run-recorder: … with TTL up to ~1800s - What it is: causal-ordering/coordination keys used by the “task-run-recorder” processing scope. Expect subkeys like: - task-run-recorderprocessing<event_id> (short-lived in-flight guard) - task-run-recorderseen<event_id> (mark that a leader event was seen) - task-run-recorderevent<event_id>, task-run-recorderfollowers<leader_id>, task-run-recorder:waitlist - Why it grows: high rates of task-related events, out-of-order arrival (followers waiting on leaders), backlog/bursts. Keys exist during a “lookback” window to maintain ordering. - Expected TTL: up to the ordering lookback window (commonly around 30 minutes), plus a few seconds for in-flight markers. They auto-expire. - triggers: … with TTL up to ~1800s - What it is: the same causal-ordering key family but for the “triggers” processing scope (automation trigger evaluation). - Why it grows: lots of automations/triggers firing, out-of-order/missing predecessor events, or bursts. Same lookback behavior/TTL as above. What usage patterns make these accumulate - High event volume within the dedup window (message:*): - Every unique published event id within ~5 minutes creates a dedup key. If you emit a lot of events (task state changes, flow state changes, custom events), you’ll see a large active set here. They will decay as the window advances. - Heavy automation/trigger activity (triggers:*): - Many triggers evaluating concurrently, or triggers that depend on prior events, create ordering/“waitlist” entries. Bursty or out-of-order delivery increases followers and waitlist keys. - Chatty task lifecycle events (task-run-recorder:*): - Lots of tasks starting/finishing/retrying → more ordering keys. Out-of-order or delayed events increase waitlist/followers until leaders are seen or the lookback TTL expires. - Out-of-order or missing events: - If a “leader” event (e.g., a parent or earlier event) is delayed or never arrives, followers wait in Redis until it shows up or the lookback TTL elapses. This spikes counts under the scope prefixes. - Consumers behind or down: - If a consumer lags, waitlists and processing markers stick around longer, increasing counts temporarily. What’s “normal” vs. concerning - message:* in the hundreds of thousands can be normal if you publish that many unique events in a 5-minute window; they should steadily decay. - task-run-recorder:* and triggers:* in the tens/hundreds of thousands suggest high throughput and/or some out-of-order waiting. If keys keep growing without decaying over the lookback TTL, check consumer health and event delivery order. - events/actions being a single key each is normal; measure stream depth with XLEN and ensure trimming is active. Operational checks and tuning ideas
- Verify decay: sample TTL and counts over time to ensure message:* drops within ~5 minutes and the task-run-recorder/triggers sets drop within your lookback window (~30 minutes in your snapshot). - Check consumer lag: - Are the event/automation consumers healthy and keeping up? Lag increases waitlists/followers. - Stream depth: - Inspect XLEN for events/actions to see if streams are growing faster than consumers can ack. Prefect trims streams periodically; deep streams indicate lag. - Reduce out-of-order arrivals: - If you batch or retry publishers, ensure stable id assignment and clocks; out-of-order increases waitlist/followers. - Lookback window trade-off: - A longer lookback increases Redis residency but tolerates more reordering. If your environment is stable, reducing it decreases the number of active ordering keys (be cautious and test). References - Prefect Events concepts: Events docs - Prefect Automations: Automations docs - Prefect Redis integration (code): prefect-redis If you can share: - Approx events/sec (or flow/task runs/hour) - Whether you’re using Redis for Prefect’s messaging backend - Any recent consumer lag or automation spikes …I can help interpret whether your counts line up with expected load and suggest concrete tuning.
s
describe the consumers for each of these @Marvin. I don't believe they're streams by the way.
m
thought for 64.5 seconds
You’re right that most of what you listed are not streams. In a standard Prefect 3.x + Redis setup: - events and actions are Redis streams (single key each, no TTL) - message:*, task-run-recorder:*, and triggers:* are regular keys used for dedup/ordering state Here’s who “consumes” or uses each, and how they interact. - message:* - What it is: short-lived deduplication keys created by publishers (e.g., event publishers) to avoid re-publishing the same message/id within a lookback window. - Who uses it: producers only. There isn’t a consumer process; publishers do a set-if-not-exists with TTL and move on. Keys expire automatically. - events (likely a Redis stream) - What it is: the main event bus carrying flow/task lifecycle and custom events. - Who consumes it: - Task-run recorder: reads events to maintain task/flow lineage and related state; it writes the task-run-recorder:* ordering/coordination keys while it works. - Triggers/automations evaluator: reads the same events to evaluate automations/triggers; it writes the triggers:* ordering/coordination keys and, when a trigger fires, publishes actions to the actions stream. - Note: Each of these typically has its own consumer group so they each see every event. - task-run-recorder:* (not a stream) - What it is: causal-ordering/coordination keys for the “task run recorder” processing scope. - Examples include processing:<event_id>, seen:<event_id>, event:<event_id>, followers:<leader_id>, waitlist. - Who uses it: the task-run recorder consumer itself. It writes and reads these keys to ensure in-order processing and to resolve out-of-order arrivals. No separate consumer exists for these keys. - triggers:* (not a stream) - What it is: the same family of ordering/coordination keys but for the “triggers” (automations) processing scope. - Who uses it: the automations/trigger evaluation consumer. It writes/reads these keys to maintain ordering and bucketization while evaluating triggers. No separate consumer exists here either. - actions (likely a Redis stream) - What it is: a work queue for concrete actions to execute (e.g., start a flow run, call a webhook, send a notification). - Who consumes it: the actions runner/executor service. You can scale multiple action consumers; they typically share a single consumer group to distribute work. When actions are executed, they’re acked and the stream is trimmed over time. How to confirm in your Redis - Check key types: -
TYPE events
-
TYPE actions
- You should see
stream
for these; the others will be
string
,
set
, or
zset
depending on the coordination structure. - Inspect consumer groups: -
XINFO GROUPS events
-
XINFO GROUPS actions
- Stream depth and consumers: -
XLEN events
-
XINFO CONSUMERS events <group-name>
- Sample coordination keys: -
SCAN 0 MATCH "task-run-recorder:*" COUNT 1000
-
SCAN 0 MATCH "triggers:*" COUNT 1000
If you can share whether you’re using the Redis messaging backend and what you see from
TYPE events
and
TYPE actions
, I can confirm the exact consumer group layout in your deployment. References - Events: Prefect Events - Automations: Automations overview - Redis integration code: prefect-redis
c
Hey @Marvin is it safe to set MAXLEN on the events stream?
m
thought for 200.9 seconds
Short answer: I don’t recommend setting MAXLEN on the events stream. Why: - Prefect’s Redis consumers trim with XTRIM MINID, not MAXLEN. They compute the lowest last-delivered-id across all consumer groups and only trim messages that every group has already seen. MAXLEN ignores consumer group state and can delete messages that some groups haven’t processed yet—leading to dropped events and broken automations. What to do instead: - Let Prefect handle trimming and tune it: -
PREFECT_REDIS_MESSAGING_CONSUMER_TRIM_EVERY
(default 60s) – how often consumers trim -
PREFECT_REDIS_MESSAGING_CONSUMER_TRIM_IDLE_THRESHOLD
(default 300s) – excludes fully idle groups from blocking trims - Keep consumer groups healthy (remove abandoned ones, ensure consumers are running). Idle/stale groups can block trimming; Prefect excludes very idle groups and cleans up empty ephemeral groups automatically. - If you need to constrain memory, prefer Redis-wide memory policies over per-stream MAXLEN, and monitor stream depth. How to verify your setup:
Copy code
TYPE events
XLEN events
XINFO GROUPS events
XINFO CONSUMERS events <group-name>
If your
events
key isn’t a stream (TYPE != stream), then MAXLEN doesn’t apply; share the TYPE output and I’ll help confirm. References - Events concepts: Prefect Events docs - Automations overview: Automations docs - Redis integration code: prefect-redis