Queue Support
Recording audit logs involves database writes — including hash computation and a locked transaction in full mode. By default, AuditChain dispatches these writes to a queued job so they do not slow down HTTP requests. The queue is enabled out of the box; this page covers how it works and when you might want to change the defaults.
Configuration
// config/audit-chain.php 'queue' => [ 'enabled' => true, 'connection' => null, 'queue' => 'default', ],
| Key | Default | Description |
|---|---|---|
queue.enabled |
true |
Dispatch audit recording to a queued job |
queue.connection |
null |
Queue connection (null = application default) |
queue.queue |
default |
Queue name for audit jobs |
How It Works
When queue is enabled, audit log creation follows this flow:
- The Eloquent event fires (created, updated, deleted, etc.)
- The audit payload is built synchronously (old/new values, user, IP, user agent, context, batch UUID)
- A
RecordAuditLogjob is dispatched to the queue - The queue worker picks up the job and writes the audit log to the database
The RecordAuditLog job implements ShouldQueue and includes sensible defaults:
- 3 retries on failure
- 5 second backoff between retries
- 30 second timeout
Dedicated Queue
To avoid audit recording competing with other queued jobs (emails, notifications, exports), use a dedicated queue:
'queue' => [ 'enabled' => true, 'connection' => 'redis', 'queue' => 'auditing', ],
Then run a dedicated worker for audit jobs:
php artisan queue:work redis --queue=auditing
The isChained Property
The RecordAuditLog job uses an isChained property to distinguish between light mode (activity log) and full mode (audit trail) recording:
class RecordAuditLog implements ShouldQueue { public function __construct( public readonly array $auditData, public readonly bool $isChained = false, ) {} }
This property is intentionally named isChained rather than chained to avoid a conflict with Laravel's Queueable trait, which defines its own $chained property for job chaining. If the property were named chained, it would collide with the trait and cause unexpected behavior.
When isChained is true, the job calls DatabaseDriver::storeChained() which runs the hash chain computation inside a locked transaction. When false, it calls DatabaseDriver::store() for a simple insert with null hashes.
When to Disable Queuing
Set queue.enabled to false to record audit logs synchronously:
'queue' => [ 'enabled' => false, ],
Disable queuing when:
- You are not running a queue worker — If no worker is processing jobs, audit logs will sit in the queue indefinitely and never be recorded.
- You need immediate audit visibility — Some workflows require that the audit log exists in the database before the HTTP response is sent (e.g., a compliance API that returns the audit log ID).
- Testing — Synchronous recording is simpler to test against. AuditChain's own test suite runs with queue disabled.
- Low-traffic applications — If the overhead of a synchronous database write is negligible for your use case, the simplicity of synchronous recording may be preferable.
When to Enable Queuing
Keep queuing enabled (the default) when:
- High-traffic applications — Offloading writes to a queue worker keeps HTTP response times consistent.
- Audit trail mode with locking —
HasAuditTrailuseslockForUpdate()to maintain chain ordering. Under high concurrency, this lock can briefly delay requests. A queue serializes these writes naturally. - Separate database connections — If your audit database is on a different server, the network round-trip is better absorbed by a background worker than by the request cycle.
Queue Failures
The RecordAuditLog job implements a failed() method that logs a critical error when a job permanently fails after all retries:
Log::critical('Audit log permanently lost after retries', [...])
This is important for audit completeness — you should monitor for these critical log entries in your logging infrastructure (e.g., via log aggregation alerts). A permanently failed audit job means an event occurred but was never recorded, which could create gaps in your audit trail.
If a queued audit job fails after all retries, it also lands in Laravel's failed_jobs table. Monitor this table (or use Laravel Horizon for Redis queues) to catch audit recording failures.
Missing audit logs in a hash chain will cause audit:verify to report chain breaks. Scheduled verification with --notify will alert you to these gaps. See Notifications for setup details.