Repository navigation
Releases
v0.6.0
Sixth experiment: an AI agent's tool after a provider failover.
An agent of the Laravel AI SDK 1.4.0 runs in a queued job with a primary and a backup provider. Its tool, SendInvoiceEmail, runs on the primary; the next step gets a 429. prompt() then starts the whole run over on the backup, the model asks for the tool again, and the invoice goes out twice. The job succeeds and the rate limit isn't reported, so the only trace is the AgentFailedOver event. stream() already refuses to fail over once it has sent output; prompt() has no such check. With the fix from fix/no-failover-after-tools, the tool runs once and the job fails with RateLimitedException.
Both providers are a fake OpenAI-compatible server (bin/fake-ai-provider.php), so no model or API key is needed. Like experiments 4 and 5 it runs on the database queue, no broker: vendor/bin/phpunit tests/AgentFailoverTest.php.
Checked in CI with PHP 8.3–8.5, Laravel 13 and laravel/ai 1.4.0.
v0.5.0
Fifth experiment: maxExceptions and a timeout when the worker isn't killed.
With Worker::$killOnTimeout = false (laravel/framework#61591), a job that runs past its timeout gets a TimeoutExceededException instead of a dead worker. The timeout is counted against maxExceptions twice: once by the SIGALRM handler, and again when the exception comes out of the job. A job with maxExceptions = 2 fails after its first timeout, while a job that simply throws fails after its second attempt, as it should. With the fix from fix/timeout-exception-count, both fail after two attempts.
Like experiment 4 it runs on the database queue, no broker: vendor/bin/phpunit tests/MaxExceptionsTimeoutTest.php.
Checked in CI with PHP 8.3–8.5 and Laravel 13.37.
v0.4.0
Fourth experiment, about Laravel's own queue: debounce with maxWait under steady dispatches.
#[DebounceFor(2, maxWait: 6)], a dispatch every 0.5 s for 60 s, one queue:work --sleep=3. On Laravel 13.37 every job that maxWait forced to run was discarded as superseded, because the next dispatch came before the worker took it. The job didn't run once while dispatches kept coming; the longest wait was about 60 s instead of 6. With the fix from fix/debounce-forced-job applied, it ran every 6–7 s.
It runs on the database queue with SQLite, so it needs no broker: vendor/bin/phpunit tests/DebounceMaxWaitTest.php.
Other changes:
bin/lab debounce <seconds> <ms>dispatches the debounced job;LAB_DATABASEpoints the database queue at an SQLite file.bin/labconnects to RabbitMQ only for the commands that need it.- The CI job is called "Experiments" now.
Checked in CI with PHP 8.3–8.5 and Laravel 13.37.
v0.3.0
Third experiment: what queue:work and rabbitmq:consume do while they are paused: in maintenance mode, after SIGUSR2, or with queue:pause.
queue:workstops at once on SIGTERM and within--sleeponqueue:restart, also while it is paused.rabbitmq:consumeacts on SIGTERM andqueue:restartonly afterartisan upor SIGCONT, and runs one more job first. While it is paused, it holds the messages RabbitMQ sends it, up to the prefetch count, so another worker on the same queue gets none of them. It ignoresqueue:pause.- An idle
rabbitmq:consumedoes restart onqueue:restarton these versions (vyuldashev/laravel-queue-rabbitmq#587).
Other changes:
- Every experiment gives its workers a storage directory of their own (
LAB_STORAGE) with a file cache, so a manualartisan downdoesn't reach them. - The event logs also record
JobPopping,WorkerPausingandWorkerQueuePaused. - The README starts with a summary of the findings and has charts drawn from the event logs.
Checked in CI with PHP 8.3–8.5, Laravel 13.37, php-amqplib 3.7.5 and RabbitMQ 3.13.3. Until vyuldashev/laravel-queue-rabbitmq#680 is released, the package is installed from its branch.
v0.2.0
Second experiment: what queue:work and rabbitmq:consume do when RabbitMQ stops answering but TCP stays up. The broker is frozen with docker compose pause for 100 s while idle workers with a 15 s heartbeat wait for jobs.
queue:workwaits inbasic_getuntil the broker is back:channel_rpc_timeoutdefaults to 0, and a wait without a timeout never gets to php-amqplib's heartbeat checks.--timeoutand--max-timedon't cover this wait either.- With heartbeats and a 10 s
channel_rpc_timeout,queue:workstops after 42 s withLostConnectionand exit code 0. The same timeout without heartbeats only reports an error every 14 s. rabbitmq:consumedoesn't notice: its non-blocking poll counts the client's own heartbeats as activity (vyuldashev/laravel-queue-rabbitmq#673). With--max-timeit stops consuming but can't close the channel until the broker is back; withchannel_rpc_timeoutit exits with code 1.
Other changes:
- The lab uses the package's default connection path.
LAB_HEARTBEAT,LAB_IO_TIMEOUTandLAB_RPC_TIMEOUTset the connection options of a process. bin/lab artisanhandles an exception from a command likephp artisanin an application: it reports and prints it and exits with code 1.- The results of experiment 1 are the same.
Checked in CI with PHP 8.3–8.5, Laravel 13.37, php-amqplib 3.7.5 and RabbitMQ 3.13.3. Until vyuldashev/laravel-queue-rabbitmq#680 is released, the package is installed from its branch.
v0.1.0
First experiment: what queue:work and rabbitmq:consume do when a job outlives RabbitMQ's delivery acknowledgement timeout.
- Both commands exit with code 0 after the 406 instead of hanging.
- The slow job runs twice. With
rabbitmq:consumeand the default prefetch, the jobs already in the client buffer run twice as well.
Checked in CI with PHP 8.3–8.5, Laravel 13.37, php-amqplib 3.7.5 and RabbitMQ 3.13.3. Until vyuldashev/laravel-queue-rabbitmq#680 is released, the package is installed from its branch.