Gateway Pressure Telemetry

See the gateway backing up before it drops work — admission, queues, dead-letter and loop-lag on health()

Pressure telemetry surfaces how loaded the gateway is before it starts shedding turns. One health block, one classification.

Diagram

Gateway Pressure Telemetry

🎟️ Admission

⚖️ evaluate_pressure

📥 Inbox

📤 Outbox

⏱️ Loop lag

📊 nominal / elevated / saturated

Quick Start

  1. Ask an agent
    python
    from praisonaiagents import Agent
    
    agent = Agent(
        name="Ops",
        instructions="Read gateway.health() and tell the operator if the gateway is backing up.",
    )
    agent.start("Is the gateway saturated right now?")
  2. Read the health block
    python
    h = gateway.health()
    pressure = h.get("pressure")
    if pressure:
        print(pressure["pressure"])            # 'nominal' | 'elevated' | 'saturated'
        print(pressure["admission_in_flight"], "/", pressure["admission_max"])
        print(pressure["event_loop_lag_p99_ms"], "ms")
  3. Classify your own numbers
    python
    from praisonaiagents.gateway import evaluate_pressure
    
    p = evaluate_pressure(
        admission={"max_concurrent_runs": 16, "in_flight": 15, "queued": 40, "shed": 3},
        inbox_pending=128,
        outbox_pending=412,
        outbox_dead_letter=2,
        loop_lag_p99_ms=740.0,
    )
    print(p.pressure)   # 'saturated'

How It Works

The enforcement layer already measures every field. This block reads them back and folds them through a pure classifier.

Diagram
evaluate_pressureAdmission / Outbox / WatchdogGatewayOperatorevaluate_pressureAdmission / Outbox / WatchdogGatewayOperatorhealth()stats(), depths, last_lag_msraw numbersevaluate_pressure(…)HealthPressure(pressure=”saturated”)health()[“pressure”] = {…}
SignalWhere it comes from
admission_*AdmissionGate.stats()
inbox_pendingSum of per-session inbox depth
outbox_pending, outbox_dead_letterOutboundQueue.try_depth_snapshot() (never blocks the loop)
event_loop_lag_p99_msLoopWatchdog.last_lag_ms

The block is omitted entirely when no back-pressure machinery is wired — always-on gateways see the block appear naturally.

The resulting health surface under load:

python
h = gateway.health()
# {'status': 'healthy', ...,
#  'pressure': {'admission_max': 16, 'admission_in_flight': 15,
#     'admission_queued': 40, 'admission_shed_total': 3,
#     'inbox_pending': 128, 'outbox_pending': 412, 'outbox_dead_letter': 2,
#     'event_loop_lag_p99_ms': 740.0, 'pressure': 'saturated'}}

Choosing what to alert on

Diagram

Overload is imminent

Load is building

Delivery is failing

Loop hangs but not wedged

What do you want to catch?

Alert on pressure==’saturated’

Alert on pressure==’elevated’ for N minutes

Alert on outbox_dead_letter > 0

Alert on event_loop_lag_p99_ms >= 500


Configuration Options

Nothing to configure — pressure telemetry is on wherever the back-pressure machinery is on.

The fields on HealthPressure:

FieldTypeDefaultDescription
admission_maxint0Configured concurrency ceiling (0 = unbounded).
admission_in_flightint0Turns currently running.
admission_queuedint0Turns waiting for a slot.
admission_shed_totalint0Cumulative turns shed since start.
inbox_pendingint0Aggregate pending inbound queue depth across sessions.
outbox_pendingint0Durable outbound backlog awaiting delivery.
outbox_dead_letterint0Durable outbound entries in the dead-letter tier.
event_loop_lag_p99_msfloat0.0Recent event-loop scheduling lag (ms).
pressure"nominal" \"elevated" \"saturated""nominal"Derived classification.

HealthPressure is frozen=True — mutating a field raises dataclasses.FrozenInstanceError.

Thresholds

saturated (any one):

  • admission full and work is queuing (in_flight >= max and queued > 0)
  • outbox_dead_letter > 0
  • event_loop_lag_p99_ms >= 500.0

elevated (any one):

  • admission utilisation >= 0.8 (80%)
  • queued > 0
  • inbox_pending > 0
  • outbox_pending > 0
  • event_loop_lag_p99_ms >= 100.0

nominal — none of the above.

Unbounded admission (max_concurrent_runs == 0) never divides by zero. Negative or non-numeric inputs are coerced to zero.


Metrics

Six gauges appear on /metrics whenever the block is populated:

MetricSource field
admission_in_flightpressure.admission_in_flight
admission_queuedpressure.admission_queued
inbox_pendingpressure.inbox_pending
outbox_pendingpressure.outbox_pending
outbox_dead_letterpressure.outbox_dead_letter
event_loop_lag_mspressure.event_loop_lag_p99_ms

Common Patterns

Autoscaler / load-balancer readiness gate

python
p = gateway.health().get("pressure", {})
ready = p.get("pressure") != "saturated"

Agent-driven ops assistant

python
from praisonaiagents import Agent

def get_pressure() -> dict:
    return gateway.health().get("pressure", {})

agent = Agent(
    name="Ops",
    instructions="If pressure=='saturated', tell the operator what to check first.",
    tools=[get_pressure],
)
agent.start("How is the gateway holding up?")

Pre-flight classification

python
from praisonaiagents.gateway import evaluate_pressure

# Fold your own metrics through the same classifier the gateway uses
p = evaluate_pressure(loop_lag_p99_ms=210.0)
print(p.pressure)   # 'elevated'

Best Practices

Page on saturated, not elevated

saturated is imminent shed. A non-empty dead-letter tier means outbound work is being dropped — page immediately.

Unit-test with evaluate_pressure

Pure and deterministic — feed it stub numbers and assert the classification. No fixtures needed.

Guard the pressure key

The pressure key is absent when no back-pressure machinery is wired. Guard with .get("pressure").

Snapshot vs shape over time

health()["pressure"] is a point-in-time snapshot; the /metrics gauges give you the shape over time.


Related