Skip to content

Commit f666b25

Browse files
v2: allow sync/missing queue driver when task_dispatch_mode=poll
Backend capability reporting flagged `queue_sync_unsupported` and `queue_connection_missing` as hard errors regardless of dispatch mode. In poll mode tasks are delivered to workers over HTTP, so the queue backend is unused for task delivery and a sync or missing queue connection is a legitimate configuration. Downgrade both codes from 'error' to 'info' when workflows.v2.task_dispatch_mode=poll so: - BackendCapabilities::isSupported() returns true - /api/health reports the backend as 'ok' - TaskBackendCapabilities::recordClaimFailureIfUnsupported() stops marking task claims as failed with last_claim_error=queue_sync_unsupported - capabilities.requires_worker flips to false so clients know the queue worker is not a prerequisite Queue mode behaviour is unchanged — sync/missing queues still produce error-level issues there. Three new BackendCapabilitiesTest cases pin the contract. Closes #286. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 20a0108 commit f666b25

2 files changed

Lines changed: 74 additions & 5 deletions

File tree

src/V2/Support/BackendCapabilities.php

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,19 +103,30 @@ private static function queue(?string $configuredConnection = null): array
103103
: self::normalize(config(sprintf('queue.connections.%s.driver', $connection)));
104104
$issues = [];
105105

106+
// In poll mode, tasks are delivered to workers via HTTP poll rather than
107+
// through a Laravel queue, so a sync or missing queue driver is an
108+
// acceptable configuration — the queue backend is simply unused for
109+
// task delivery. We still surface an informational note so operators
110+
// can see what will happen if they later flip back to queue mode.
111+
$pollMode = self::normalize(config('workflows.v2.task_dispatch_mode')) === 'poll';
112+
106113
if ($connection === null || $driver === null) {
107114
$issues[] = self::issue(
108115
'queue',
109-
'error',
116+
$pollMode ? 'info' : 'error',
110117
'queue_connection_missing',
111-
'Workflow v2 requires a configured asynchronous queue connection for durable task delivery.',
118+
$pollMode
119+
? 'No queue connection is configured; task dispatch runs in poll mode so workers receive tasks over HTTP instead of a queue worker.'
120+
: 'Workflow v2 requires a configured asynchronous queue connection for durable task delivery.',
112121
);
113122
} elseif ($driver === 'sync') {
114123
$issues[] = self::issue(
115124
'queue',
116-
'error',
125+
$pollMode ? 'info' : 'error',
117126
'queue_sync_unsupported',
118-
'Workflow v2 requires an asynchronous queue worker; the sync queue driver executes jobs inline and cannot provide the worker/lease boundary.',
127+
$pollMode
128+
? 'Queue driver is [sync], which is acceptable in poll mode because tasks are delivered over HTTP instead of a queue worker.'
129+
: 'Workflow v2 requires an asynchronous queue worker; the sync queue driver executes jobs inline and cannot provide the worker/lease boundary.',
119130
);
120131
}
121132

@@ -126,7 +137,7 @@ private static function queue(?string $configuredConnection = null): array
126137
'capabilities' => [
127138
'async_delivery' => $driver !== null && $driver !== 'sync',
128139
'delayed_delivery' => $driver !== null && $driver !== 'sync',
129-
'requires_worker' => true,
140+
'requires_worker' => ! $pollMode,
130141
'max_delay_seconds' => $driver === 'sqs' ? 900 : null,
131142
'after_commit_option' => $connection === null ? null : config(
132143
sprintf('queue.connections.%s.after_commit', $connection)

tests/Unit/V2/BackendCapabilitiesTest.php

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,62 @@ public function testMysqlDatabaseDoesNotPublishConcurrencyNote(): void
149149

150150
$this->assertArrayNotHasKey('concurrent_write_safety', $snapshot['structural_limits']['backend_adjustments']);
151151
}
152+
153+
// ---------------------------------------------------------------
154+
// Poll-mode queue relaxations (#286)
155+
// ---------------------------------------------------------------
156+
157+
public function testPollModeAllowsSyncQueueDriver(): void
158+
{
159+
config()->set('workflows.v2.task_dispatch_mode', 'poll');
160+
config()->set('queue.default', 'sync');
161+
config()->set('queue.connections.sync.driver', 'sync');
162+
163+
$snapshot = BackendCapabilities::snapshot();
164+
165+
$this->assertTrue($snapshot['queue']['supported']);
166+
$this->assertTrue(BackendCapabilities::isSupported($snapshot));
167+
$this->assertFalse($snapshot['queue']['capabilities']['requires_worker']);
168+
169+
$queueIssue = collect($snapshot['issues'])
170+
->firstWhere('code', 'queue_sync_unsupported');
171+
172+
$this->assertNotNull($queueIssue, 'The queue_sync_unsupported note should still be recorded informationally.');
173+
$this->assertSame('info', $queueIssue['severity']);
174+
}
175+
176+
public function testPollModeAllowsMissingQueueConnection(): void
177+
{
178+
config()->set('workflows.v2.task_dispatch_mode', 'poll');
179+
config()->set('queue.default', null);
180+
181+
$snapshot = BackendCapabilities::snapshot();
182+
183+
$this->assertTrue($snapshot['queue']['supported']);
184+
$this->assertTrue(BackendCapabilities::isSupported($snapshot));
185+
186+
$queueIssue = collect($snapshot['issues'])
187+
->firstWhere('code', 'queue_connection_missing');
188+
189+
$this->assertNotNull($queueIssue);
190+
$this->assertSame('info', $queueIssue['severity']);
191+
}
192+
193+
public function testQueueModeStillRejectsSyncQueueDriver(): void
194+
{
195+
config()->set('workflows.v2.task_dispatch_mode', 'queue');
196+
config()->set('queue.default', 'sync');
197+
config()->set('queue.connections.sync.driver', 'sync');
198+
199+
$snapshot = BackendCapabilities::snapshot();
200+
201+
$this->assertFalse($snapshot['queue']['supported']);
202+
$this->assertFalse(BackendCapabilities::isSupported($snapshot));
203+
204+
$queueIssue = collect($snapshot['issues'])
205+
->firstWhere('code', 'queue_sync_unsupported');
206+
207+
$this->assertNotNull($queueIssue);
208+
$this->assertSame('error', $queueIssue['severity']);
209+
}
152210
}

0 commit comments

Comments
 (0)