Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Dispatcher;

use App\Message\SomeMessage;
use Ibexa\Bundle\Messenger\Stamp\DeduplicateStamp;
use Symfony\Component\Messenger\MessageBusInterface;

Expand All @@ -14,11 +15,11 @@ public function __construct(MessageBusInterface $bus)
$this->bus = $bus;
}

public function schedule(object $message): void
public function schedule(): void
{
$this->bus->dispatch($message);
$this->bus->dispatch(new SomeMessage());

$deduplicationKey = 'my_message.project.<key_based_on_message>';
$this->bus->dispatch($message, [new DeduplicateStamp($deduplicationKey)]);
$this->bus->dispatch(new SomeMessage(), [new DeduplicateStamp($deduplicationKey)]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php declare(strict_types=1);

namespace App\Messenger;

use App\Message\SomeMessage;
use Ibexa\Contracts\Messenger\Transport\MessageProviderInterface;

final class SomeMessageProvider implements MessageProviderInterface
{
public function getHandledClasses(): iterable
{
return [SomeMessage::class];
}
}
4 changes: 2 additions & 2 deletions docs/administration/configuration/dynamic_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

## ConfigResolver

Dynamic configuration is handled by a ConfigResolver.
Dynamic configuration is handled by the [`ConfigResolverInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-SiteAccess-ConfigResolverInterface.html).

Check notice on line 9 in docs/administration/configuration/dynamic_configuration.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/administration/configuration/dynamic_configuration.md#L9

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/administration/configuration/dynamic_configuration.md", "range": {"start": {"line": 9, "column": 23}}}, "severity": "INFO"}

It exposes the `hasParameter()` and `getParameter()` methods.
You can use them to check the different *scopes* available for a given *namespace* to find the appropriate parameter.
Expand Down Expand Up @@ -68,7 +68,7 @@
arguments: ['@ibexa.config.resolver']
```

You can also use the [autowire feature]([[= symfony_doc =]]/service_container/autowiring.html), by type hinting against ConfigResolverInterface.
You can also use the [autowire feature]([[= symfony_doc =]]/service_container/autowiring.html), by type hinting against [`ConfigResolverInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-SiteAccess-ConfigResolverInterface.html).

Check failure on line 71 in docs/administration/configuration/dynamic_configuration.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/administration/configuration/dynamic_configuration.md#L71

[Ibexa.Spellcheck] Did you really mean 'autowire'?
Raw output
{"message": "[Ibexa.Spellcheck] Did you really mean 'autowire'?", "location": {"path": "docs/administration/configuration/dynamic_configuration.md", "range": {"start": {"line": 71, "column": 23}}}, "severity": "ERROR"}

For more information about dependency injection, see [Service container](php_api.md#service-container).

Expand Down
101 changes: 84 additions & 17 deletions docs/infrastructure_and_maintenance/background_tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@

1. A message PHP object is dispatched, for example, `ProductPriceReindex`.
2. The message is wrapped in an envelope, which may contain additional metadata, called [stamps](#stamps).
3. The message is placed in the transport queue.
3. The message is placed in the [transport queue](#route-message-to-background-queue).

Check notice on line 97 in docs/infrastructure_and_maintenance/background_tasks.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/infrastructure_and_maintenance/background_tasks.md#L97

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/infrastructure_and_maintenance/background_tasks.md", "range": {"start": {"line": 97, "column": 16}}}, "severity": "INFO"}
It can be a Doctrine table, a Redis/Valkey queue, and so on.
4. A worker process continuously reads messages from the queue, pulls them into the default bus `ibexa.messenger.bus` and assigns them to the right handler.
5. A handler service processes the message (executes the command).
Expand Down Expand Up @@ -134,10 +134,17 @@
Use a process manager of your choice to run the following command, or make it start together with the server:

``` bash
php bin/console messenger:consume ibexa.messenger.transport --bus=ibexa.messenger.bus --siteaccess=<OPTIONAL>`
php bin/console messenger:consume ibexa.messenger.transport --bus=ibexa.messenger.bus --siteaccess=<OPTIONAL>
```

In [multi-repository setups](repository_configuration.md), the worker process always works for a [SiteAccess](multisite_configuration.md#siteaccess-configuration) that you indicate by using the `--siteaccess` option, therefore you may need to run multiple workers, one for each SiteAccess.
Use the `--siteaccess` option to set the default [SiteAccess](multisite_configuration.md#siteaccess-configuration) and [repository](repository_configuration.md#defining-custom-connection) for the worker process.
The worker uses this SiteAccess for every message that doesn't have a [`SiteAccessStamp`](#siteaccessstamp).
Comment on lines +140 to +141

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we hare reco about what should be the default SiteAccess?
I don't remember why I used admin in https://doc.ibexa.co/en/5.0/getting_started/install_with_ddev/#configure-background-tasks-optional

@mnocon mnocon Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Steveb-p what do you think, which SiteAccess should the worker use by default - can we recommend something?

I guess it doesn't matter, as long it's one SA per each repository configured and SiteAccess stamps are added correctly


If a message has a `SiteAccessStamp`, the worker uses the SiteAccess from the stamp instead to process this message.
Thanks to this, one worker process can handle messages coming from different SiteAccesses.

In [multi-repository setups](repository_configuration.md), run one worker process for each repository.
With this setup, each worker process can connect to the right database.
Comment on lines +146 to +147

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, which siteaccess to pick-up? A random front or the back office?


!!! caution "Multi-repository setups"

Expand All @@ -152,21 +159,25 @@
To have a task processed in the background by [[= product_name_base =]] Messenger:

1. Inject the `ibexa.messenger.bus` service as an object implementing the `Symfony\Component\Messenger\MessageBusInterface` interface.
2. Dispatch an appropriate message by using the `MessageBusInterface::dispatch()` method, exactly as described in [Symfony Messenger documentation]([[= symfony_doc =]]/messenger.html#dispatching-the-message).
2. Dispatch an appropriate message, for example a [custom message](#register-custom-message-and-handler), by using the `MessageBusInterface::dispatch()` method, exactly as described in [Symfony Messenger documentation]([[= symfony_doc =]]/messenger.html#dispatching-the-message).

``` yaml
services:
SomeClassThatSchedulesExecutionInTheBackground:
arguments:
$bus: '@ibexa.messenger.bus'
```
``` yaml
services:
SomeClassThatSchedulesExecutionInTheBackground:
arguments:
$bus: '@ibexa.messenger.bus'
```

``` php
[[= include_code('code_samples/background_tasks/src/Dispatcher/SomeClassThatSchedulesExecutionInTheBackground.php', 1, 19, remove_indent=True) =]]
[[= include_code('code_samples/background_tasks/src/Dispatcher/SomeClassThatSchedulesExecutionInTheBackground.php', 23, 24, remove_indent=True) =]]
```
``` php
[[= include_code('code_samples/background_tasks/src/Dispatcher/SomeClassThatSchedulesExecutionInTheBackground.php', 1, 5, indent_level=1) =]]
[[= include_code('code_samples/background_tasks/src/Dispatcher/SomeClassThatSchedulesExecutionInTheBackground.php', 7, 20, indent_level=1) =]]
[[= include_code('code_samples/background_tasks/src/Dispatcher/SomeClassThatSchedulesExecutionInTheBackground.php', 24, 25, indent_level=1) =]]
```

Additionally, attach message metadata by using [stamps](#stamps).
3. [Route the message to the background queue](#route-message-to-background-queue).
Otherwise the bus calls the handler immediately, in the same process that dispatches the message.

4. Additionally, attach message metadata by using [stamps](#stamps).

### Stamps

Expand All @@ -184,6 +195,7 @@
On top of the supported Symfony stamps, [[= product_name =]] provides the following ones:

- [`DeduplicateStamp`](#deduplicatestamp)
- [`SiteAccessStamp`](#siteaccessstamp)

#### DeduplicateStamp

Expand All @@ -193,18 +205,45 @@
This stamp is backported from Symfony 7.
For more information, see [Symfony 7.4 documentation about message deduplication](https://symfony.com/doc/7.4//messenger.html#message-deduplication).

#### SiteAccessStamp

[`Ibexa\Contracts\Messenger\Stamp\SiteAccessStamp`](https://example.com/add-link-when-php-api-reference-is-generated) contains the name of the [SiteAccess](siteaccess.md) that dispatched the message.

You don't need to add this stamp manually, [[= product_name_base =]] Messenger attaches this stamp to each dispatched message automatically.
The stamp contains the SiteAccess that is current at the moment of dispatch.

Before the worker calls the handler, it changes the configuration scope to the SiteAccess from the stamp.
The handler then reads [SiteAccess-aware configuration](multisite_configuration.md#siteaccess-configuration) for the SiteAccess that dispatched the message, and not for the SiteAccess that the worker process started with.

!!! caution "The stamp doesn't change the current SiteAccess"

The stamp changes the configuration scope only.
It doesn't change the SiteAccess in the `Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface` service.
`SiteAccessServiceInterface::getCurrent()` always returns the SiteAccess that the worker process started with, for all messages.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note: Will be fixed in ibexa/core#798 / 6.0.

We could also make SiteAccessService to listen to CONFIG_SCOPE_CHANGE events in 5.x and react accordingly, and backport the stack feature from 6.0. In theory this is a bug (SiteAccessService::getCurrent() returning wrong site access), so we would not exactly break BC promise - but initially I thought we shouldn't.

@konradoboza @alongosz @ibexa/php-dev ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, if I understand correctly, this note can be removed in 5.0 and 6.0 when ibexa/core#805 is merged? Depending on whether it gets merged before or after the release, in this PR or in a follow-up.


To get a SiteAccess-aware value in a handler, use the [`ConfigResolverInterface` service](dynamic_configuration.md).

## Extend Ibexa Messenger

To handle a custom use case with background tasks, you need the following elements:

- a message class to hold the data
- a handler class to perform the task, registered on the `ibexa.messenger.bus` bus
- a message provider to [route the message to the transport queue](#route-message-to-background-queue)
- code to [dispatch the message](#dispatch-message)

If you don't route the message to the transport queue, the bus calls the handler synchronously, in the same process that dispatches the message.

### Register custom message and handler

To handle additional use cases with background tasks, you can create [custom message and handler class]([[= symfony_doc =]]/messenger.html#creating-a-message-handler):
To handle additional use cases with background tasks, first create a [custom message and handler class]([[= symfony_doc =]]/messenger.html#creating-a-message-handler):

``` php
[[= include_code('code_samples/background_tasks/src/Message/SomeMessage.php') =]]
```

``` php
[[= include_file("code_samples/background_tasks/src/MessageHandler/SomeHandler.php") =]]
[[= include_file('code_samples/background_tasks/src/MessageHandler/SomeHandler.php') =]]
```

Add a service definition to `config/services.yaml` and set the `bus` to `ibexa.messenger.bus`:
Expand All @@ -216,3 +255,31 @@
- name: messenger.message_handler
bus: ibexa.messenger.bus
```

At this point the handler processes the messages synchronously.
To move the work to the background, [route the message to the background queue](#route-message-to-background-queue).

### Route message to background queue

To process the message in the background, send it to a transport queue.
[[= product_name_base =]] Messenger uses message providers instead of [Symfony `framework.messenger.routing` configuration]([[= symfony_doc =]]/messenger.html#routing-messages-to-a-transport).

A message provider is a service that implements the [`MessageProviderInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Messenger-Transport-MessageProviderInterface.html) interface, and the `getHandledClasses()` method must return the list of message classes that [[= product_name_base =]] Messenger must send to the queue to process in the background.

The `getHandledClasses()` method can also return a parent class or an interface.
In this case, all messages that extend this class, or implement this interface, go to the background queue.

To send `SomeMessage` to the background queue, create the following provider:
Comment thread
adriendupuis marked this conversation as resolved.

``` php hl_lines="12"
[[= include_file('code_samples/background_tasks/src/Messenger/SomeMessageProvider.php') =]]
```

If you're not using service autoconfiguration, add the `ibexa.messenger.sender_message_provider` tag to the service:

Check notice on line 278 in docs/infrastructure_and_maintenance/background_tasks.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/infrastructure_and_maintenance/background_tasks.md#L278

[Ibexa.ByUsing] Prefer 'by using' or 'with' to plain 'using'.
Raw output
{"message": "[Ibexa.ByUsing] Prefer 'by using' or 'with' to plain 'using'.", "location": {"path": "docs/infrastructure_and_maintenance/background_tasks.md", "range": {"start": {"line": 278, "column": 11}}}, "severity": "INFO"}

``` yaml hl_lines="4"
services:
App\Messenger\SomeMessageProvider:
tags:
- name: ibexa.messenger.sender_message_provider

Check notice on line 284 in docs/infrastructure_and_maintenance/background_tasks.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/infrastructure_and_maintenance/background_tasks.md#L284

[Ibexa.Lists] Verify list formatting: Full sentences should start with uppercase and end with a period. Sentence fragments should start with lowercase and have no period.
Raw output
{"message": "[Ibexa.Lists] Verify list formatting: Full sentences should start with uppercase and end with a period. Sentence fragments should start with lowercase and have no period.", "location": {"path": "docs/infrastructure_and_maintenance/background_tasks.md", "range": {"start": {"line": 284, "column": 1}}}, "severity": "INFO"}
```
3 changes: 2 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -989,8 +989,9 @@ extra:
latest_tag_5_0: '5.0.9'

symfony_doc: 'https://symfony.com/doc/5.x'
user_doc: 'https://doc.ibexa.co/projects/userguide/en/4.6'
symfony_version: '5.4'

user_doc: 'https://doc.ibexa.co/projects/userguide/en/4.6'
connect_doc: 'https://doc.ibexa.co/projects/connect/en/latest'

extra_css:
Expand Down
Loading