Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/phpunit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ jobs:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
ports:
- 5672
- 5672:5672
options: >-
--health-cmd="rabbitmq-diagnostics -q ping"
--health-interval=10s
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
"php": "^8.1"
},
"require-dev": {
"codeigniter4/devkit": "^1.0",
"codeigniter4/devkit": "^1.3",
"codeigniter4/framework": "^4.3",
"predis/predis": "^2.0",
"phpstan/phpstan-strict-rules": "^1.5",
"phpstan/phpstan-strict-rules": "^2.0",
"php-amqplib/php-amqplib": "^3.7"
},
"minimum-stability": "dev",
Expand Down
5 changes: 0 additions & 5 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ Available options:
- [$redis](#redis)
- [$predis](#predis)
- [$rabbitmq](#rabbitmq)
- [$keepDoneJobs](#keepdonejobs)
- [$keepFailedJobs](#keepfailedjobs)
- [$queueDefaultPriority](#queuedefaultpriority)
- [$queuePriorities](#queuepriorities)
Expand Down Expand Up @@ -77,10 +76,6 @@ The configuration settings for `rabbitmq` handler. You need to have [php-amqplib
* `password` - The password for authentication. Default value: `guest`.
* `vhost` - The virtual host to use. Default value: `/`.

### $keepDoneJobs

If the job is done, should we keep it in the table? Default value: `false`.

### $keepFailedJobs

If the job failed, should we move it to the failed jobs table? Default value: `true`.
Expand Down
166 changes: 166 additions & 0 deletions docs/events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# Events

The Queue library provides a comprehensive event system that allows you to monitor and react to various queue operations. Events are triggered at key points in the queue lifecycle, enabling you to implement logging, monitoring, alerting, or custom business logic.

---

## Overview

Queue events are built on top of CodeIgniter's native event system and are emitted automatically by the queue handlers and workers. Each event carries contextual information about what happened, when it happened, and any relevant data.

Events allow you to:

- **Monitor** queue performance and job execution
- **Log** queue activities for debugging and auditing
- **Alert** administrators when jobs fail or workers stop
- **Collect metrics** for analytics and reporting
- **Implement custom logic** based on queue operations

## Available Events

All event names are available as constants in the `CodeIgniter\Queue\Events\QueueEventManager` class.

### Job Events

Events related to individual job lifecycle:

| Event | Constant | When Triggered | Metadata |
|-------|----------|----------------|----------|
| `queue.job.pushed` | `JOB_PUSHED` | A job is added to the queue | `job_class`, `job` |
| `queue.job.push.failed` | `JOB_PUSH_FAILED` | Failed to push a job to the queue | `job_class`, `exception` |
| `queue.job.processing.started` | `JOB_PROCESSING_STARTED` | A worker starts processing a job | `job_class`, `job`, `worker_id` |
| `queue.job.processing.completed` | `JOB_PROCESSING_COMPLETED` | A job completes successfully | `job_class`, `job`, `processing_time`, `worker_id` |
| `queue.job.failed` | `JOB_FAILED` | A job fails after exhausting all retry attempts | `job_class`, `job`, `exception`, `processing_time`, `worker_id` |

### Worker Events

Events related to queue worker lifecycle:

| Event | Constant | When Triggered | Metadata |
|-------|----------|----------------|----------|
| `queue.worker.started` | `WORKER_STARTED` | A queue worker starts processing | `priorities`, `config`, `worker_id` |
| `queue.worker.stopped` | `WORKER_STOPPED` | A queue worker stops | `priorities`, `uptime_seconds`, `jobs_processed`, `worker_id`, `stop_reason`, `memory_usage`, `memory_peak` |

Worker stop reasons include:

- `signal_stop` - Stopped by signal (SIGTERM, SIGINT, etc.)
- `memory_limit` - Memory limit reached
- `time_limit` - Max time limit reached
- `job_limit` - Max jobs processed
- `planned_stop` - Scheduled stop via `queue:stop` command
- `empty_queue` - Queue is empty (with `--stop-when-empty` flag)

### Handler Events

Events related to queue handler connections:

| Event | Constant | When Triggered | Metadata |
|-------|----------|----------------|----------|
| `queue.handler.connection.established` | `HANDLER_CONNECTION_ESTABLISHED` | Successfully connected to queue backend | `config` |
| `queue.handler.connection.failed` | `HANDLER_CONNECTION_FAILED` | Failed to connect to queue backend | `exception`, `config` |

### Operation Events

Events related to queue operations:

| Event | Constant | When Triggered | Metadata |
|-------|----------|----------------|----------|
| `queue.cleared` | `QUEUE_CLEARED` | Queue is cleared via `queue:clear` command | None |

## Listening to Events

You can listen to queue events using CodeIgniter's standard event system. Add your listeners in `app/Config/Events.php`:

```php
<?php

use CodeIgniter\Events\Events;
use CodeIgniter\Queue\Events\QueueEvent;
use CodeIgniter\Queue\Events\QueueEventManager;

// Listen to job completion
Events::on(QueueEventManager::JOB_PROCESSING_COMPLETED, static function (QueueEvent $event) {
log_message('info', 'Job completed: {job} in {time}s', [
'job' => $event->getJobClass(),
'time' => $event->getProcessingTime(),
]);
});

// Listen to job failures
Events::on(QueueEventManager::JOB_FAILED, static function (QueueEvent $event) {
log_message('error', 'Job failed: {job} - {error}', [
'job' => $event->getJobClass(),
'error' => $event->getExceptionMessage(),
]);
});

// Listen to worker lifecycle
Events::on(QueueEventManager::WORKER_STARTED, static function (QueueEvent $event) {
log_message('info', 'Worker started for queue: {queue}', [
'queue' => $event->getQueue(),
]);
});

Events::on(QueueEventManager::WORKER_STOPPED, static function (QueueEvent $event) {
$metadata = $event->getAllMetadata();

log_message('info', 'Worker stopped: {reason}, processed {jobs} jobs in {time}s', [
'reason' => $metadata['stop_reason'],
'jobs' => $metadata['jobs_processed'],
'time' => round($metadata['uptime_seconds'], 2),
]);
});
```

## Event Object

All event listeners receive a `QueueEvent` object with the following methods:

### Basic Information

```php
$event->getType(); // Event type (e.g., 'queue.job.pushed')
$event->getHandler(); // Handler name (e.g., 'database', 'redis')
$event->getQueue(); // Queue name (e.g., 'emails', 'default')
$event->getTimestamp(); // Time object when event occurred
```

### Event Type Checks

```php
$event->isJobEvent(); // True for job-related events
$event->isWorkerEvent(); // True for worker-related events
$event->isConnectionEvent(); // True for connection-related events
$event->isOperationEvent(); // True for operation events
```

### Job Information (for job events)

```php
$event->getJobId(); // Job ID
$event->getJobClass(); // Fully qualified job class name
$event->getPriority(); // Job priority
$event->getAttempts(); // Number of attempts
$event->getStatus(); // Job status
$event->getProcessingTime(); // Processing time in seconds
$event->getProcessingTimeMs(); // Processing time in milliseconds
```

### Error Information (for failed events)

```php
$event->getException(); // Exception object (if any)
$event->getExceptionMessage(); // Exception message
$event->hasFailed(); // True if event contains an exception
```

### Metadata Access

```php
// Get specific metadata
$event->getMetadata('worker_id');
$event->getMetadata('priority', 'default');

// Get all metadata
$metadata = $event->getAllMetadata();
```
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ If you use `RabbitMQ` (you still need a relational database to store failed jobs
* [Basic usage](basic-usage.md)
* [Running queues](running-queues.md)
* [Commands](commands.md)
* [Events](events.md)
* [Troubleshooting](troubleshooting.md)

### Acknowledgements
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,5 @@ nav:
- Basic usage: basic-usage.md
- Running queues: running-queues.md
- Commands: commands.md
- Events: events.md
- Troubleshooting: troubleshooting.md
5 changes: 4 additions & 1 deletion phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ parameters:
allRules: false
disallowedLooseComparison: true
booleansInConditions: true
disallowedConstructs: true
disallowedBacktick: true
disallowedEmpty: true
disallowedImplicitArrayCreation: true
disallowedShortTernary: true
matchingInheritedMethodNames: true

1 change: 1 addition & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
<directory suffix=".php">./src/</directory>
</include>
<exclude>
<file>./src/Compatibility/SignalTrait.php</file>
<directory suffix=".php">./src/Commands/Generators</directory>
<directory suffix=".php">./src/Commands/Utils</directory>
<directory suffix=".php">./src/Config</directory>
Expand Down
5 changes: 5 additions & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@
cacheDirectory="build/psalm/"
findUnusedBaselineEntry="true"
findUnusedCode="false"
ensureOverrideAttribute="false"
>
<projectFiles>
<directory name="src/" />
<directory name="tests/" />
<ignoreFiles>
<directory name="vendor" />
<file name="src/Compatibility/SignalTrait.php" />
</ignoreFiles>
</projectFiles>
<stubs>
<file name="stubs/SignalTrait.phpstub" />
</stubs>
</psalm>
10 changes: 7 additions & 3 deletions rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,18 @@
use Rector\CodingStyle\Rector\ClassMethod\MakeInheritedMethodVisibilitySameAsParentRector;
use Rector\CodingStyle\Rector\FuncCall\CountArrayToEmptyArrayComparisonRector;
use Rector\Config\RectorConfig;
use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodRector;
use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPromotedPropertyRector;
use Rector\EarlyReturn\Rector\Foreach_\ChangeNestedForeachIfsToEarlyContinueRector;
use Rector\EarlyReturn\Rector\If_\ChangeIfElseValueAssignToEarlyReturnRector;
use Rector\EarlyReturn\Rector\If_\RemoveAlwaysElseRector;
use Rector\EarlyReturn\Rector\Return_\PreparedValueToEarlyReturnRector;
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
use Rector\Php73\Rector\FuncCall\StringifyStrNeedlesRector;
use Rector\Php81\Rector\ClassMethod\NewInInitializerRector;
use Rector\PHPUnit\AnnotationsToAttributes\Rector\Class_\AnnotationWithValueToAttributeRector;
use Rector\PHPUnit\AnnotationsToAttributes\Rector\ClassMethod\DataProviderAnnotationToAttributeRector;
use Rector\PHPUnit\CodeQuality\Rector\Class_\YieldDataProviderRector;
use Rector\PHPUnit\CodeQuality\Rector\MethodCall\AssertEmptyNullableObjectToAssertInstanceofRector;
use Rector\PHPUnit\Set\PHPUnitSetList;
use Rector\Privatization\Rector\Property\PrivatizeFinalClassPropertyRector;
use Rector\Set\ValueObject\LevelSetList;
Expand Down Expand Up @@ -95,8 +96,11 @@
// Supported from PHPUnit 10
DataProviderAnnotationToAttributeRector::class,

NewInInitializerRector::class => [
'src/Payloads/Payload.php',
AssertEmptyNullableObjectToAssertInstanceofRector::class,

// Skip onInterruption method - called dynamically via reflection in SignalTrait
RemoveUnusedPrivateMethodRector::class => [
__DIR__ . '/src/Commands/QueueWork.php',
],
]);

Expand Down
Loading
Loading