Chalama Reddy
03/22/2026, 11:54 PMMarvin
03/22/2026, 11:55 PMMarvin
03/22/2026, 11:58 PMexport PREFECT_REDIS_MESSAGING_HOST="redis-host"
export PREFECT_REDIS_MESSAGING_PORT="6379"
export PREFECT_REDIS_MESSAGING_DB="0"
export PREFECT_MESSAGING_BROKER="prefect_redis.messaging"
export PREFECT_MESSAGING_CACHE="prefect_redis.messaging"
export PREFECT_SERVER_EVENTS_CAUSAL_ORDERING="prefect_redis.ordering"
export PREFECT_SERVER_CONCURRENCY_LEASE_STORAGE="prefect_redis.lease_storage"
Docs:
- Run a local Prefect server
- Scale self-hosted Prefect (multi-worker requirements)
- Prefect Redis integration
If you need HA/failover, common patterns are to use a managed Redis that presents a single endpoint or put a proxy in front of a clustered/sentinel setup so Prefect still sees one host:port. If that’s your goal, happy to discuss options.Chalama Reddy
03/23/2026, 12:39 AMMarvin
03/23/2026, 12:39 AMMarvin
03/23/2026, 12:43 AMXADD re-raises an exception. The API does not swallow it, so the client gets an HTTP error and those events are not stored or queued by Prefect.
- Consume-time failure (Redis becomes unavailable later):
- Events already in the Redis Stream are durable.
- The event persister/consumers use a reconnect loop with backoff and resume from the stream once Redis is back.
- Message processing failures (handler errors) are retried up to a limit; after that they go to a DLQ (for Redis backend, the DLQ is stored in Redis). This DLQ is not used for publish-time outages.
Implications and mitigations
- To avoid data loss when Redis is down at publish time:
- Add client-side retry with exponential backoff around POST /events (whatever is emitting your events should retry on non-2xx).
- Run a highly-available Redis endpoint (managed Redis with automatic failover, or a proxy that presents a single stable host:port). Prefect does not natively support Redis Cluster or Sentinel, so HA needs to be provided behind one endpoint.
- Monitor Redis and Prefect server health so you can remediate quickly.
- For critical signals, consider dual-writing to your own durable store (e.g., object storage or a logging pipeline) so you have an audit trail even if Redis is briefly unavailable.
Docs
- Run a local Prefect server
- Scale self-hosted Prefect (multi-worker + Redis requirements)
- Prefect Redis integration
If you want, I can share a minimal retry snippet you can wrap around event POSTs, or we can talk through HA options for your environment.Chalama Reddy
03/23/2026, 1:11 AMMarvin
03/23/2026, 1:11 AMMarvin
03/23/2026, 1:12 AMChalama Reddy
03/23/2026, 1:14 AMMarvin
03/23/2026, 1:14 AMMarvin
03/23/2026, 1:16 AMid so retries are idempotent.
- Minimal event fields to include safely: event, resource, occurred, id.
Python (requests, synchronous)
import time
import uuid
import random
import requests
from datetime import datetime, timezone
API_URL = "<http://127.0.0.1:4200/api/events>"
def now_iso():
return datetime.now(timezone.utc).isoformat()
def post_events_with_retry(events, max_attempts=8, base_sleep=0.5, max_sleep=30, timeout=(3.0, 10.0)):
"""
Exponential backoff with jitter. Retries on 5xx and connection errors.
- max_attempts: total tries including the first
- base_sleep: initial sleep in seconds
- max_sleep: cap for backoff
- timeout: (connect_timeout, read_timeout)
"""
attempt = 0
while True:
attempt += 1
try:
resp = <http://requests.post|requests.post>(API_URL, json=events, timeout=timeout)
# Retry on 5xx; 2xx/4xx return immediately
if 500 <= resp.status_code < 600:
raise RuntimeError(f"Server error {resp.status_code}: {resp.text}")
resp.raise_for_status()
return resp
except (requests.exceptions.RequestException, RuntimeError) as e:
if attempt >= max_attempts:
raise
# Exponential backoff with jitter
sleep = min(max_sleep, base_sleep * (2 ** (attempt - 1)))
sleep += random.uniform(0, sleep * 0.25) # jitter up to 25%
time.sleep(sleep)
# Build an idempotent example event
event = {
"event": "example.event.occurred",
"resource": {"prefect.resource.id": "example.resource:123"},
"occurred": now_iso(),
"id": str(uuid.uuid4())
}
# Send with retries
response = post_events_with_retry([event])
print("Status:", response.status_code) # 204 on success
Python (httpx, async)
import asyncio
import uuid
import random
import httpx
from datetime import datetime, timezone
API_URL = "<http://127.0.0.1:4200/api/events>"
def now_iso():
return datetime.now(timezone.utc).isoformat()
async def post_events_with_retry_async(events, max_attempts=8, base_sleep=0.5, max_sleep=30.0, timeout=10.0):
attempt = 0
async with httpx.AsyncClient(timeout=timeout) as client:
while True:
attempt += 1
try:
resp = await <http://client.post|client.post>(API_URL, json=events)
if 500 <= resp.status_code < 600:
raise RuntimeError(f"Server error {resp.status_code}: {resp.text}")
resp.raise_for_status()
return resp
except (httpx.RequestError, httpx.HTTPStatusError, RuntimeError):
if attempt >= max_attempts:
raise
sleep = min(max_sleep, base_sleep * (2 ** (attempt - 1)))
sleep += random.uniform(0, sleep * 0.25)
await asyncio.sleep(sleep)
event = {
"event": "example.event.occurred",
"resource": {"prefect.resource.id": "example.resource:123"},
"occurred": now_iso(),
"id": str(uuid.uuid4())
}
async def main():
resp = await post_events_with_retry_async([event])
print("Status:", resp.status_code)
asyncio.run(main())
Bash (curl) quick-and-dirty backoff loop
```
#!/usr/bin/env bash
set -euo pipefail
API_URL="http://127.0.0.1:4200/api/events"
# Build a minimal JSON payload inline; replace UUID as needed
PAYLOAD='[
{
"event": "example.event.occurred",
"resource": { "prefect.resource.id": "example.resource:123" },
"occurred": "'$(date -u +"%Y-%m-%dT%H:%M:%SZ")'",
"id": "550e8400-e29b-41d4-a716-446655440000"
}
]'
attempt=0
max_attempts=8
sleep_secs=0.5
max_sleep=30
while true; do
attempt=$((attempt+1))Marvin
03/23/2026, 1:16 AMid) so retries are idempotent server-side.
- Consider retrying on connection errors/timeouts and 5xx; usually don’t retry 4xx.
- Keep a reasonable cap on backoff (e.g., 30–60s) and a max attempts or total timeout.
- Log failures and payload IDs so you can reconcile if retries ultimately fail.
More on events: Prefect Events