Skip to main content
The turn lock keeps only one turn running against a session at a time, and its redis backend extends that guarantee across every replica.

Quick Start

The redis backend needs the async Redis client:
1

Single replica — already correct, no config

One gateway process serialises turns with the in-process default. Nothing to set.
2

Multiple replicas — switch the backend to redis

Two or more gateway processes sharing a session need a distributed lease so two pods can’t run one session’s turns at once.
3

Or configure it in gateway.yaml


How It Works

The lock is LockMap-compatible: one .get(session_id) call returns an async context manager that holds a cluster-wide lease for the whole async with block, and a crashed holder’s lease expires after ttl so a healthy session is never wedged. Release is owner-checked and idempotent: only the exact owner token that claimed the lease may release it, so an expired or reclaimed lease is a harmless no-op, never another replica’s lease.

Fail-open on Redis outage

A live turn is never blocked by Redis being down — the lock degrades to a local asyncio.Lock for that turn and records a degraded capability you find later.
A live turn is never blocked by Redis being down; you find out via praisonai gateway doctor.
While degraded, turns serialise per-process only (cross-replica ordering pauses until Redis recovers). The lock records a degraded capability entry (owner_kind="capability", owner_id="turn_lock:redis") and clears it automatically on the first successful reconnect.

Health & observability

The fail-open behaviour surfaces through the same tools that report every other degraded gateway capability.
  • praisonai gateway doctor — lists the capability / turn_lock:redis degraded owner with a redacted reason.
  • Gateway health() / /health — the same degraded entry, machine-readable.
  • Logs — a WARNING: Distributed turn lock degraded to local-only (Redis unavailable): … on the first outage.

Extensible Doctor

How praisonai gateway doctor reports degraded capabilities.

Gateway Liveness

Companion health signal — reap half-open connections.

Configuration Options

TurnLockConfig from praisonaiagents/gateway/config.py. The enabled property returns True only when a distributed backend is selected (backend != "local").
Lease keys are namespaced as {RedisConfig.prefix}turnlock:{session_id}, defaulting to praison:turnlock: when no prefix is set. Operators sharing one Redis across environments should set distinct RedisConfig.prefix values so leases never collide.

TurnLockConfig Reference

Full field, type, and default reference for TurnLockConfig.

GatewayConfig Reference

The parent config that wires turn_lock into the gateway.

When to Enable

Enable redis whenever more than one gateway process can share a session — replica count alone decides it. You need backend: 'redis' whenever more than one gateway process shares a session — with or without an identity resolver. A single-adapter deployment with replicas > 1 still wires the distributed backend.

Backward Compatibility

local is the default and reproduces today’s in-process asyncio.Lock byte-for-byte — single-replica deployments are unchanged and add no new dependency.

Best Practices

The lease auto-renews at ~ttl/3 while a turn runs, so a turn longer than ttl never lets another replica claim the same session. Size ttl to survive a brief Redis blip, not your slowest turn — the default 60.0 suits most deployments.
Leave url unset and the distributed lock reuses the gateway’s configured RedisConfig. Set url only when the lock lives on a different Redis than push/delivery — when both are set, url wins.
On a Redis outage the lock fails open and records a turn_lock:redis degraded capability. Check praisonai gateway doctor or /health — a persistent entry means cross-replica ordering is paused until Redis recovers.
Flip backend='redis' before raising replicaCount. Scaling first leaves a window where two pods run concurrent turns on one session.

Gateway Durability

The wider “never lose or double-run a turn” story this fits into.

Gateway Degraded Channels

The symmetric fail-open pattern for a degraded delivery channel.

Cross-Platform Sessions

The in-process turn lock this extends across replicas.

Helm Chart (Gateway)

Scale the gateway on Kubernetes with replicaCount.

Gateway Admission Control

Sibling robustness knob — concurrency ceiling and backpressure.

Gateway Turn Executor

Sibling robustness knob — place a session’s turn on its own worker.
See also — idempotency store_backend: redis. Because the turn lock has a real Redis backend, operators often expect the inbound idempotency store to mirror it. It does not have a Redis backend yet — setting store_backend: redis falls back to durable SQLite and records a degraded fact. See Gateway Hooks → Multi-replica gateways for what happens if you set it.