deliveryStub = Workflow::newActivityStub( WebhookDeliveryActivityInterface::class, ActivityOptions::new() ->withStartToCloseTimeout(CarbonInterval::seconds(30)) ->withRetryOptions( RetryOptions::new() ->withMaximumAttempts(3) ->withInitialInterval(CarbonInterval::seconds(1)) ->withBackoffCoefficient(3.0) ) ); $this->deadLetterStub = Workflow::newActivityStub( WebhookDeliveryActivityInterface::class, ActivityOptions::new() ->withStartToCloseTimeout(CarbonInterval::seconds(10)) ->withRetryOptions( RetryOptions::new()->withMaximumAttempts(1) ) ); } public function deliver(array $payload, array $endpoints, array $simulationConfig = []): \Generator { // Initialize endpoint statuses foreach ($endpoints as $endpoint) { $this->endpointStatuses[$endpoint] = [ 'status' => 'pending', 'attempt' => 0, 'responseTime' => 0, ]; } // Fan-out: deliver to all endpoints in parallel $promises = []; foreach ($endpoints as $endpoint) { $this->endpointStatuses[$endpoint]['status'] = 'delivering'; $promises[$endpoint] = Workflow::async(function () use ($endpoint, $payload, $simulationConfig) { try { $result = yield $this->deliveryStub->deliverToEndpoint($endpoint, $payload, $simulationConfig); return ['endpoint' => $endpoint, 'success' => true, 'result' => (array) $result]; } catch (\Throwable $e) { return ['endpoint' => $endpoint, 'success' => false, 'error' => $e->getMessage()]; } }); } // Collect results from all parallel activities foreach ($promises as $endpoint => $promise) { $outcome = (array) (yield $promise); if ($outcome['success']) { $result = $outcome['result']; $this->endpointStatuses[$endpoint] = [ 'status' => 'delivered', 'attempt' => $result['attempt'] ?? 1, 'responseTime' => $result['responseTime'] ?? 0, ]; $this->totalDelivered++; $attempt = $result['attempt'] ?? 1; if ($attempt > 1) { $this->retryCount += ($attempt - 1); } } else { $this->endpointStatuses[$endpoint]['status'] = 'failed'; $this->totalFailed++; // Dead-letter failed deliveries yield $this->deadLetterStub->deadLetter($endpoint, $payload, $outcome['error'] ?? 'Unknown error'); $this->endpointStatuses[$endpoint]['status'] = 'dead_lettered'; $this->totalDeadLettered++; } } return [ 'status' => 'completed', 'totalDelivered' => $this->totalDelivered, 'totalFailed' => $this->totalFailed, 'totalDeadLettered' => $this->totalDeadLettered, 'retryCount' => $this->retryCount, 'endpoints' => $this->endpointStatuses, ]; } public function getDeliveryStatus(): array { return [ 'totalDelivered' => $this->totalDelivered, 'totalFailed' => $this->totalFailed, 'totalDeadLettered' => $this->totalDeadLettered, 'retryCount' => $this->retryCount, 'endpoints' => $this->endpointStatuses, ]; } }