Skip to content
Merged
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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ By default, Yii Framework uses [yiisoft/yii-console](https://github.com/yiisoft/

See [Console commands](docs/guide/en/console-commands.md) for more details.

Producers use `Yiisoft\Queue\QueueProducerInterface` (`push()`, `status()`, `getName()`); consumers use `Yiisoft\Queue\QueueConsumerInterface` (`run()`, `listen()`). See [capability configuration](docs/guide/en/queue-capabilities.md) for the strict role map used when named queues are configured.
Producers use `Yiisoft\Queue\QueueProducerInterface` (`push()`, `status()`, `getQueueName()`); consumers use `Yiisoft\Queue\QueueConsumerInterface` (`run()`, `listen()`). See [capability configuration](docs/guide/en/queue-capabilities.md) for the strict role map used when named queues are configured.

> In case you're running the queue in synchronous mode (no adapter), `queue:listen` logs an info message and exits. The messages are processed immediately when pushed.

Expand Down
2 changes: 1 addition & 1 deletion docs/guide/en/console-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ If you are using [yiisoft/config](https://github.com/yiisoft/config) and [yiisof

If you are using [symfony/console](https://github.com/symfony/console) directly, you should register the commands manually.

> **Note:** `queue:run` and `queue:listen-all` use `QueueConsumerProviderInterface::getConsumerNames()` when no queue names are passed. Explicitly passed names are resolved with `getConsumer()` and must have a consumer role.
> **Note:** `queue:run` and `queue:listen-all` use `QueueConsumerProviderInterface::getConsumerQueueNames()` when no queue names are passed. Explicitly passed names are resolved with `getConsumer()` and must have a consumer role.

In [yiisoft/app](https://github.com/yiisoft/app) the `yii` console binary is provided out of the box.
If you are using [yiisoft/yii-console](https://github.com/yiisoft/yii-console) or `symfony/console` without that template, invoke these commands the same way you invoke other console commands in your application.
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/en/queue-capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A logical queue name can independently expose a producer, a consumer, or both. Inject `QueueProducerInterface` to push/status messages and `QueueConsumerInterface` to run/listen. Console commands use only `QueueConsumerProviderInterface`; retry middleware uses a direct `QueueProducerInterface` or `QueueProducerProviderInterface`.

Named providers use a strict nested role map. `getProducerNames()` and `getConsumerNames()` return only names with that role. Role definitions are created lazily and cached per name and role; failed lazy creation is cached and repeated lookups rethrow the same configuration error.
Named providers use a strict nested role map. `getProducerQueueNames()` and `getConsumerQueueNames()` return only names with that role. Role definitions are created lazily and cached per name and role; failed lazy creation is cached and repeated lookups rethrow the same configuration error.

```php
use Yiisoft\Queue\QueueConsumer;
Expand Down
6 changes: 3 additions & 3 deletions docs/guide/en/queue-names-advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ Most applications configure names through [`yiisoft/queue.queues`](queue-names.m

Providers translate a queue name into the capability the caller needs:

- `QueueProducerProviderInterface::getProducer($name)` returns a `QueueProducerInterface` for pushing messages and obtaining their status.
- `QueueConsumerProviderInterface::getConsumer($name)` returns a `QueueConsumerInterface` for running or listening for messages.
- `hasProducer()` / `hasConsumer()` check whether a name exposes a role. `getProducerNames()` / `getConsumerNames()` list names for only that role.
- `QueueProducerProviderInterface::getProducer($queueName)` returns a `QueueProducerInterface` for pushing messages and obtaining their status.
- `QueueConsumerProviderInterface::getConsumer($queueName)` returns a `QueueConsumerInterface` for running or listening for messages.
- `hasProducer()` / `hasConsumer()` check whether a name exposes a role. `getProducerQueueNames()` / `getConsumerQueueNames()` list names for only that role.

Both lookup methods accept a string or `BackedEnum`. They throw `QueueNotFoundException` when the name is unknown or does not have the requested role. This separation prevents a producer-only queue from accidentally being used by a worker, and vice versa.

Expand Down
2 changes: 1 addition & 1 deletion docs/guide/en/queue-names.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ final readonly class SendTransactionalEmail
}
```

Both typed providers accept strings and `BackedEnum` values. Use `getProducerNames()` or `getConsumerNames()` when enumerating only that role.
Both typed providers accept strings and `BackedEnum` values. Use `getProducerQueueNames()` or `getConsumerQueueNames()` when enumerating only that role.

## Running workers

Expand Down
10 changes: 5 additions & 5 deletions src/AsyncQueueProducer.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*/
final class AsyncQueueProducer implements QueueProducerInterface
{
private string $name;
private string $queueName;
private PushMiddlewareDispatcher $dispatcher;

/**
Expand All @@ -28,35 +28,35 @@
private readonly LoggerInterface $logger,
PushMiddlewareConfig $middlewareConfig,
private readonly AdapterInterface $adapter,
string|BackedEnum $name = DefaultQueue::NAME,
string|BackedEnum $queueName = DefaultQueue::NAME,
array $middlewareDefinitions = [],
) {
$this->name = StringNormalizer::normalize($name);
$this->queueName = StringNormalizer::normalize($queueName);
$this->dispatcher = new PushMiddlewareDispatcher(
middlewareFactory: $middlewareConfig->middlewareFactory,
middlewareDefinitions: [...$middlewareConfig->commonMiddlewareDefinitions, ...$middlewareDefinitions],

Check warning on line 37 in src/AsyncQueueProducer.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "ArrayItemRemoval": @@ @@ $this->queueName = StringNormalizer::normalize($queueName); $this->dispatcher = new PushMiddlewareDispatcher( middlewareFactory: $middlewareConfig->middlewareFactory, - middlewareDefinitions: [...$middlewareConfig->commonMiddlewareDefinitions, ...$middlewareDefinitions], + middlewareDefinitions: [...$middlewareDefinitions], finishHandler: new AdapterPushHandler($adapter), ); }
finishHandler: new AdapterPushHandler($adapter),
);
}

public function getName(): string
public function getQueueName(): string
{
return $this->name;
return $this->queueName;
}

public function push(MessageInterface $message): MessageInterface
{
$this->logger->debug(

Check warning on line 49 in src/AsyncQueueProducer.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "MethodCallRemoval": @@ @@ public function push(MessageInterface $message): MessageInterface { - $this->logger->debug( - 'Preparing to push message with message type "{messageType}".', - ['messageType' => $message->getType()], - ); + $message = $this->dispatcher->dispatch($message); $id = IdEnvelope::fromMessage($message)->getId(); $this->logger->info(
'Preparing to push message with message type "{messageType}".',
['messageType' => $message->getType()],

Check warning on line 51 in src/AsyncQueueProducer.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "ArrayItemRemoval": @@ @@ { $this->logger->debug( 'Preparing to push message with message type "{messageType}".', - ['messageType' => $message->getType()], + [], ); $message = $this->dispatcher->dispatch($message); $id = IdEnvelope::fromMessage($message)->getId();

Check warning on line 51 in src/AsyncQueueProducer.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "ArrayItem": @@ @@ { $this->logger->debug( 'Preparing to push message with message type "{messageType}".', - ['messageType' => $message->getType()], + ['messageType' > $message->getType()], ); $message = $this->dispatcher->dispatch($message); $id = IdEnvelope::fromMessage($message)->getId();
);
$message = $this->dispatcher->dispatch($message);
$id = IdEnvelope::fromMessage($message)->getId();
$this->logger->info(

Check warning on line 55 in src/AsyncQueueProducer.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "MethodCallRemoval": @@ @@ ); $message = $this->dispatcher->dispatch($message); $id = IdEnvelope::fromMessage($message)->getId(); - $this->logger->info( - $id === null - ? 'Pushed message with message type "{messageType}" to the queue. ID doesn\'t assigned.' - : 'Pushed message with message type "{messageType}" to the queue. Assigned ID #{id}.', - ['messageType' => $message->getType(), 'id' => $id], - ); + return $message; }
$id === null

Check warning on line 56 in src/AsyncQueueProducer.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "Ternary": @@ @@ $message = $this->dispatcher->dispatch($message); $id = IdEnvelope::fromMessage($message)->getId(); $this->logger->info( - $id === null - ? 'Pushed message with message type "{messageType}" to the queue. ID doesn\'t assigned.' - : 'Pushed message with message type "{messageType}" to the queue. Assigned ID #{id}.', + $id === null ? 'Pushed message with message type "{messageType}" to the queue. Assigned ID #{id}.' : 'Pushed message with message type "{messageType}" to the queue. ID doesn\'t assigned.', ['messageType' => $message->getType(), 'id' => $id], ); return $message;
? 'Pushed message with message type "{messageType}" to the queue. ID doesn\'t assigned.'
: 'Pushed message with message type "{messageType}" to the queue. Assigned ID #{id}.',
['messageType' => $message->getType(), 'id' => $id],

Check warning on line 59 in src/AsyncQueueProducer.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "ArrayItemRemoval": @@ @@ $id === null ? 'Pushed message with message type "{messageType}" to the queue. ID doesn\'t assigned.' : 'Pushed message with message type "{messageType}" to the queue. Assigned ID #{id}.', - ['messageType' => $message->getType(), 'id' => $id], + ['id' => $id], ); return $message; }

Check warning on line 59 in src/AsyncQueueProducer.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.5-ubuntu-latest

Escaped Mutant for Mutator "ArrayItem": @@ @@ $id === null ? 'Pushed message with message type "{messageType}" to the queue. ID doesn\'t assigned.' : 'Pushed message with message type "{messageType}" to the queue. Assigned ID #{id}.', - ['messageType' => $message->getType(), 'id' => $id], + ['messageType' > $message->getType(), 'id' => $id], ); return $message; }
);
return $message;
}
Expand Down
16 changes: 8 additions & 8 deletions src/Command/ListenAllCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,16 @@ protected function execute(InputInterface $input, OutputInterface $output): int
/** @var string[] $queueNames */
$queueNames = $input->getArgument('queue');
if ($queueNames === []) {
$queueNames = $this->queueProvider->getConsumerNames();
$queueNames = $this->queueProvider->getConsumerQueueNames();
}

$queues = [];
/** @var string $queue */
foreach ($queueNames as $queue) {
$queues[] = $this->queueProvider->getConsumer($queue);
$consumers = [];
/** @var string $queueName */
foreach ($queueNames as $queueName) {
$consumers[] = $this->queueProvider->getConsumer($queueName);
}

if ($queues === []) {
if ($consumers === []) {
$output->writeln('No consumers are configured.');

return Command::SUCCESS;
Expand All @@ -86,8 +86,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int

while ($this->loop->canContinue()) {
$hasMessages = false;
foreach ($queues as $queue) {
$hasMessages = $queue->run((int) $input->getOption('limit')) > 0 || $hasMessages;
foreach ($consumers as $consumer) {
$hasMessages = $consumer->run((int) $input->getOption('limit')) > 0 || $hasMessages;
}

if (!$hasMessages) {
Expand Down
10 changes: 5 additions & 5 deletions src/Command/RunCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int
/** @var string[] $queueNames */
$queueNames = $input->getArgument('queue');
if ($queueNames === []) {
$queueNames = $this->queueProvider->getConsumerNames();
$queueNames = $this->queueProvider->getConsumerQueueNames();
}

/** @var string $queue */
foreach ($queueNames as $queue) {
$queueConsumer = $this->queueProvider->getConsumer($queue);
/** @var string $queueName */
foreach ($queueNames as $queueName) {
$queueConsumer = $this->queueProvider->getConsumer($queueName);

$output->write("Processing queue $queue... ");
$output->write("Processing queue $queueName... ");
$count = $queueConsumer->run((int) $input->getOption('limit'));

$output->writeln("Messages processed: $count.");
Expand Down
12 changes: 6 additions & 6 deletions src/Debug/QueueConsumerProviderProxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@ final class QueueConsumerProviderProxy implements QueueConsumerProviderInterface
{
public function __construct(private readonly QueueConsumerProviderInterface $provider, private readonly QueueCollector $collector) {}

public function getConsumer(string|BackedEnum $name): QueueConsumerInterface
public function getConsumer(string|BackedEnum $queueName): QueueConsumerInterface
{
return new QueueConsumerDecorator($this->provider->getConsumer($name), $this->collector);
return new QueueConsumerDecorator($this->provider->getConsumer($queueName), $this->collector);
}

public function hasConsumer(string|BackedEnum $name): bool
public function hasConsumer(string|BackedEnum $queueName): bool
{
return $this->provider->hasConsumer($name);
return $this->provider->hasConsumer($queueName);
}

public function getConsumerNames(): array
public function getConsumerQueueNames(): array
{
return $this->provider->getConsumerNames();
return $this->provider->getConsumerQueueNames();
}
}
6 changes: 3 additions & 3 deletions src/Debug/QueueProducerDecorator.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ public function status(string|int $id): MessageStatus
public function push(MessageInterface $message): MessageInterface
{ /** @psalm-var array{file: string, line: int} $stack */ $stack = debug_backtrace()[0];
$message = $this->queue->push($message);
$this->collector->collectPush($this->queue->getName(), $message, $stack['file'] . ':' . $stack['line']);
$this->collector->collectPush($this->queue->getQueueName(), $message, $stack['file'] . ':' . $stack['line']);
return $message;
}

public function getName(): string
public function getQueueName(): string
{
return $this->queue->getName();
return $this->queue->getQueueName();
}
}
12 changes: 6 additions & 6 deletions src/Debug/QueueProducerProviderProxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@ final class QueueProducerProviderProxy implements QueueProducerProviderInterface
{
public function __construct(private readonly QueueProducerProviderInterface $provider, private readonly QueueCollector $collector) {}

public function getProducer(string|BackedEnum $name): QueueProducerInterface
public function getProducer(string|BackedEnum $queueName): QueueProducerInterface
{
return new QueueProducerDecorator($this->provider->getProducer($name), $this->collector);
return new QueueProducerDecorator($this->provider->getProducer($queueName), $this->collector);
}

public function hasProducer(string|BackedEnum $name): bool
public function hasProducer(string|BackedEnum $queueName): bool
{
return $this->provider->hasProducer($name);
return $this->provider->hasProducer($queueName);
}

public function getProducerNames(): array
public function getProducerQueueNames(): array
{
return $this->provider->getProducerNames();
return $this->provider->getProducerQueueNames();
}
}
2 changes: 1 addition & 1 deletion src/Middleware/Push/SynchronousPushHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function __construct(

public function handlePush(MessageInterface $message): MessageInterface
{
$this->worker->process($message, $this->queue->getName(), $this->queue);
$this->worker->process($message, $this->queue->getQueueName(), $this->queue);

return $message;
}
Expand Down
40 changes: 20 additions & 20 deletions src/Provider/CompositeQueueProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,65 +29,65 @@ public function __construct(QueueProducerProviderInterface|QueueConsumerProvider
}
}

public function getProducer(string|BackedEnum $name): QueueProducerInterface
public function getProducer(string|BackedEnum $queueName): QueueProducerInterface
{
foreach ($this->producerProviders as $provider) {
if ($provider->hasProducer($name)) {
return $provider->getProducer($name);
if ($provider->hasProducer($queueName)) {
return $provider->getProducer($queueName);
}
}
throw new QueueNotFoundException(StringNormalizer::normalize($name));
throw new QueueNotFoundException(StringNormalizer::normalize($queueName));
}

public function hasProducer(string|BackedEnum $name): bool
public function hasProducer(string|BackedEnum $queueName): bool
{
foreach ($this->producerProviders as $p) {
if ($p->hasProducer($name)) {
if ($p->hasProducer($queueName)) {
return true;
}
} return false;
}

/** @return list<string> */
public function getProducerNames(): array
public function getProducerQueueNames(): array
{
$result = [];
foreach ($this->producerProviders as $provider) {
foreach ($provider->getProducerNames() as $name) {
if (!in_array($name, $result, true)) {
$result[] = $name;
foreach ($provider->getProducerQueueNames() as $queueName) {
if (!in_array($queueName, $result, true)) {
$result[] = $queueName;
}
}
} return $result;
}

public function getConsumer(string|BackedEnum $name): QueueConsumerInterface
public function getConsumer(string|BackedEnum $queueName): QueueConsumerInterface
{
foreach ($this->consumerProviders as $provider) {
if ($provider->hasConsumer($name)) {
return $provider->getConsumer($name);
if ($provider->hasConsumer($queueName)) {
return $provider->getConsumer($queueName);
}
}
throw new QueueNotFoundException(StringNormalizer::normalize($name));
throw new QueueNotFoundException(StringNormalizer::normalize($queueName));
}

public function hasConsumer(string|BackedEnum $name): bool
public function hasConsumer(string|BackedEnum $queueName): bool
{
foreach ($this->consumerProviders as $p) {
if ($p->hasConsumer($name)) {
if ($p->hasConsumer($queueName)) {
return true;
}
} return false;
}

/** @return list<string> */
public function getConsumerNames(): array
public function getConsumerQueueNames(): array
{
$result = [];
foreach ($this->consumerProviders as $provider) {
foreach ($provider->getConsumerNames() as $name) {
if (!in_array($name, $result, true)) {
$result[] = $name;
foreach ($provider->getConsumerQueueNames() as $queueName) {
if (!in_array($queueName, $result, true)) {
$result[] = $queueName;
}
}
} return $result;
Expand Down
Loading
Loading