# Queue Simplification & Modernization - Migration Guide ## Overview The queue subsystem has been refactored to improve maintainability, testability, and observability. This guide summarizes the changes and provides migration steps. ## Changes Summary ### Phase 1-4: Core Simplifications (v2.0) #### 1. **ID Generation** (`IdGeneratorInterface`) - **What**: Centralized ID generation via `RandomIdGenerator` - **Why**: Testability, determinism, consistency across backends - **Where**: `BaseQueue::generateId()`, all queue implementations - **BC**: No breaking changes (internal only) #### 2. **JobEnvelope Factory** (`JobEnvelope::fromBackend()`) - **What**: Unified envelope creation with normalized metadata - **Why**: Reduces duplication, standardizes meta keys (`backend`, `rawId`, `status`) - **Where**: All `watch()` methods in Redis, Database, Beanstalk, ServiceBus - **BC**: Backward compatible (existing `fromDecoded()` still works) #### 3. **QueueManager** (Registry/Factory) - **What**: Singleton registry for queue backend instances - **Why**: Eliminates scattered `new XxxQueue()`, enables mocking, caches instances - **Usage**: `QueueManager::instance()->get('redis')` or `->getDefault()` - **Where**: `QueueRunCommand`, `EnqueuableTrait` - **BC**: Internal change, no API breakage #### 4. **DelayResult, Priority** - **DelayResult**: Value object for delay calculation (seconds + scheduledAt) - **Priority**: Enum for symbolic priorities (LOW/MEDIUM/HIGH → numeric) - **Why**: Eliminates 20+ lines of duplicated delay logic, prepares for advanced scheduling - **Where**: `BaseQueue::calculateDelay()` returns `DelayResult` - **BC**: Fully compatible ### Phase 5: Serialization & Observability #### 5. **PayloadSerializer** (`JsonPayloadSerializer`) - **What**: Centralized JSON serialization with schema versioning - **Features**: - Automatic `_schemaVersion` injection - Migration support for legacy payloads - Validation of required fields - **Why**: Future-proof schema evolution, centralized encoding/decoding - **Where**: All `enqueue()` and `watch()` methods - **BC**: Fully compatible (version field optional) #### 6. **InstrumentedQueueDecorator** - **What**: Transparent metrics wrapper for any queue backend - **Metrics**: - `queue_enqueue_total` (counter: success/error) - `queue_fetch_total` / `queue_fetch_empty_total` - `queue_ack_total` / `queue_nack_total` - `queue_enqueue_duration_seconds` (histogram) - `queue_fetch_duration_seconds` (histogram) - **Usage**: `new InstrumentedQueueDecorator($queue, $metrics, 'redis')` - **Why**: Unified observability without per-backend code changes - **BC**: Optional decorator, no breaking changes ## Migration Steps ### For Library Users **No action required** for typical usage. All changes are backward compatible. #### Optional Enhancements: 1. **Enable Metrics Instrumentation**: ```php use Daycry\Jobs\Libraries\QueueManager; use Daycry\Jobs\Libraries\InstrumentedQueueDecorator; use Daycry\Jobs\Metrics\Metrics; $queue = QueueManager::instance()->get('redis'); $instrumented = new InstrumentedQueueDecorator($queue, Metrics::get(), 'redis'); // Use $instrumented instead of $queue ``` 2. **Schema Versioning** (optional): ```php use Daycry\Jobs\Libraries\JsonPayloadSerializer; $serializer = new JsonPayloadSerializer(schemaVersion: 2); $queue->setSerializer($serializer); ``` 3. **Custom ID Generator** (testing): ```php use Daycry\Jobs\Interfaces\IdGeneratorInterface; class SequentialIdGenerator implements IdGeneratorInterface { private int $counter = 0; public function generate(int $bytes = 8, ?string $prefix = null): string { return ($prefix ?? '') . '-' . ++$this->counter; } } $queue->setIdGenerator(new SequentialIdGenerator()); ``` ### For Contributors #### Removed Code - `RedisQueue::failedKey()` – unused method removed (failed job tracking will be reimplemented) - Direct `json_encode/json_decode` in queue enqueue/watch – replaced with serializer #### Behavior Changes - **`removeJob($job, $recreate = true)`** in all backends now re-enqueues directly to the **same backend** instance instead of calling `$job->push()` which could route to a different worker via `QueueManager`. This ensures requeued jobs stay in the same queue backend (important for tests and multi-backend setups). #### New Extension Points - `PayloadSerializerInterface::migrate()` – override for custom schema migrations - `Priority::parse()` – symbolic → numeric priority mapping - `QueueManager::register()` – runtime worker registration ## Testing All 90 existing tests pass with the refactored code. New abstractions are validated through: - Existing queue cycle tests (Redis, Database, Beanstalk) - Envelope normalization tests - Services helper tests ## Performance Impact - **Positive**: Singleton QueueManager caches instances (no repeated instantiation) - **Neutral**: Serializer adds ~5% overhead vs raw `json_encode` (negligible) - **Positive**: Delay calculation simplified (fewer branches) ## Rollback If issues arise, revert to previous version. No database schema changes; all changes are code-level. ## Future Enhancements Prepared groundwork for: - Ack/Nack settlement model (FetchedJob abstraction) - Delayed promotion interface standardization - Advanced priority scheduling - Payload schema versioning for smooth upgrades