LifecycleWatchdog
Worker-recycling actor that triggers a graceful ActorSystem::shutdown() when any LifecycleThresholds limit is reached — memory budget, uptime ceiling, or cumulative message count.
What it does
Long-running PHP processes leak memory over time. The standard defence is to exit cleanly after N messages, X bytes, or T seconds and let the process manager (systemd, supervisor, Kubernetes) restart. LifecycleWatchdog replaces messenger:consume --limit/--memory-limit/--time-limit flags with a plain supervised actor — no symfony/console required.
The watchdog self-ticks on a fixed $checkInterval (default 5 s). On each tick it evaluates three thresholds against current state:
| Threshold | Source | Comparison |
|---|---|---|
memoryLimitBytes | memory_get_usage(true) (or custom $memoryProbe) | >= limit |
messageLimit | Cumulative count received via MessagesProcessed | >= limit |
timeLimit | Elapsed seconds since actor startup | >= limit |
A null threshold is disabled. All comparisons are inclusive: reaching the limit exactly triggers a breach.
On breach:
- Logs an info message with the breach reason.
- Dispatches a
WorkerRecyclingTriggered($reason)PSR-14 event via the actor system's event dispatcher. - Increments the
nexus.messenger.worker.recyclescounter ({recycle}). - Spawns a task to call
$system->shutdown($shutdownTimeout)(default 10 s). - Stops itself (
BehaviorWithState::stopped()).
LifecycleWatchdog + ReceiverActor integration: pass the watchdog ref as the $processedListener parameter of ReceiverActor::create(). The receiver sends a MessagesProcessed($count) report after each busy tick; the watchdog accumulates the counts into its stateful message total.
Factory
use Monadial\Nexus\Messenger\Lifecycle\LifecycleWatchdog;
LifecycleWatchdog::create(
system: ActorSystem $system,
thresholds: LifecycleThresholds $thresholds,
checkInterval: ?Duration $checkInterval = null,
shutdownTimeout: ?Duration $shutdownTimeout = null,
memoryProbe: ?Closure $memoryProbe = null,
): Behavior
| Parameter | Type | Default | Description |
|---|---|---|---|
$system | ActorSystem | — | The system to shut down when a threshold is breached. |
$thresholds | LifecycleThresholds | — | The limits to evaluate on each tick. |
$checkInterval | ?Duration | Duration::seconds(5) | How often the watchdog evaluates thresholds. |
$shutdownTimeout | ?Duration | Duration::seconds(10) | Deadline passed to $system->shutdown(). |
$memoryProbe | ?Closure(): int | memory_get_usage(true) | Override the memory measurement. Return bytes. |
LifecycleThresholds
LifecycleThresholds is an immutable value object. Build it via none() and chain wither methods:
use Monadial\Nexus\Messenger\Lifecycle\LifecycleThresholds;
use Monadial\Nexus\Runtime\Duration;
// All disabled
LifecycleThresholds::none();
// Memory only
LifecycleThresholds::none()->withMemoryLimit(128 * 1024 * 1024); // 128 MiB
// Combined
LifecycleThresholds::none()
->withMessageLimit(10_000)
->withMemoryLimit(256 * 1024 * 1024)
->withTimeLimit(Duration::seconds(3600));
| Method | Parameter | Description |
|---|---|---|
LifecycleThresholds::none() | — | Create a thresholds object with all limits disabled. |
withMemoryLimit(int $bytes) | Bytes | Trigger when memory_get_usage(true) >= $bytes. |
withMessageLimit(int $count) | Count | Trigger when cumulative processed messages >= $count. |
withTimeLimit(Duration $limit) | Duration | Trigger when uptime >= the limit (second precision). |
Public properties (readonly): $memoryLimitBytes: ?int, $messageLimit: ?int, $timeLimit: ?Duration.
Example
use Monadial\Nexus\Messenger\MessengerBridge;
use Monadial\Nexus\Messenger\Lifecycle\LifecycleThresholds;
use Monadial\Nexus\Runtime\Duration;
$watchdogRef = $system->spawn(
MessengerBridge::watchdogProps(
system: $system,
thresholds: LifecycleThresholds::none()
->withMessageLimit(10_000)
->withMemoryLimit(128 * 1024 * 1024),
checkInterval: Duration::seconds(5),
shutdownTimeout: Duration::seconds(10),
),
'watchdog',
);
// Wire the watchdog as the processedListener so it accumulates message counts
$system->spawn(
MessengerBridge::receiverProps(
receiver: $transport,
router: $router,
processedListener: $watchdogRef,
),
'orders-receiver',
);
Full API reference
LifecycleWatchdog · LifecycleThresholds
See also
- nexus-messenger package — bridge overview and full wiring guide
- ActorSystem —
shutdown()method called on threshold breach - Messenger bridge guide — worker recycling section
- Config — LifecycleThresholds — threshold parameter reference
- ReceiverActor — sends
MessagesProcessedreports to this watchdog