Problem/Motivation
The method responsible for instantiating the queue backend service for each queue worker \Drupal\Core\Queue\QueueFactory::get uses settings.php to determine if there is service set for requested worker name: 'queue_reliable_service_' . $name and 'queue_service_' . $name.
This means that if a module implements QueueWorker plugin it has no way of saying that the worker should use different backend service instead of the default database one.
This means that either the module overrides the whole queue factory service or possibly the settings service. Either way it is not pleasant.
A concrete example of where this would be useful is for queue items that process orders (e.g. in commerce). Heavier processes should make use of the queue so they are processed behind the scenes. However, if an order is loaded, it may be important to process those items immediately so the order is loaded 'up to date'. The obvious way to do this is to use a queue backend that stores the order ID in a separate field that can be used to query/filter other queue backend methods (e.g. claimItem).
To achieve this currently requires an entirely separate queue process (which means it wont be picked up by cron, drush queue-run etc) or to override the queue factory or settings service.
Proposed resolution
So instead of having hardcoded variable in settings.php I propose a new attribute is added to the QueueWorker annotation class backend_service. This would default to the current queue.database service if no value would be provided.
In the aforementioned method the queue worker plugin would be loaded(already in memory if called from Cron) and if it exists(I'm not sure if there can be a queue that does not have worker set up) and the service name would be retrieved from the plugin definition.
If there is no worker the default database backend would be used.
Remaining tasks
Decide if this is something we are happy to do.
User interface changes
None.
API changes
This solution is backwards compatible without any disruptive changes to the API, but developers wishing to make use of it can define their back end and annotate their worker to utilise it.
Data model changes
None.
Issue fork drupal-2821989
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #1
Anonymous (not verified) commentedivanjaros created an issue. See original summary.
Comment #2
sarmiliboyz commentedcurrent active development branch is 8.4.x-dev
Comment #3
dawehnerAre there specific usecases you have in mind which are not possible right now? I think it would be conceptually wrong to have to couple queue plugins to concrete queues. IMHO separating the configuration step from the queue plugin itself is exactly how it was designed to work.
Comment #4
Anonymous (not verified) commentedMy use case was that I had a specific tasks in a queue in a cloud and specific plugins to process these tasks but switching to that cloud queue backend would switch all Drupal tasks which was not what I wanted. I wanted to keep the Drupal stuff in local db backend but these specific tasks outside in remote queue.
Comment #5
dawehnerIsn't that the reason why you can have different queue configurations?
'queue_service_' . $nameComment #6
Anonymous (not verified) commentedYes, but the point of this issue is to define this in the queue worker plugin, not in the settings.php.
Comment #8
malcolm_p commentedWhile I can understand the desire to decouple workers from the backend service. I've also encountered a need to use a custom queue implementation (extending DatabaseQueue). Managing this through settings.php adds some additional integration requirements to other sites using this queue that I don't think should be necessary.
As long as an explicit backend_service is not required I think this would be useful.
Comment #9
dawehnerIt would still be nice to have a issue summary which describes WHY this feature is needed / helpful.
Comment #12
andrewbelcher commentedI've updated the IS summary with a concrete example, which for my case is process heavy actions on orders, but that need to be reliable run if an order is about to be processed.
Comment #13
bojanz commentedIn the meantime, I suggest look at/using https://www.drupal.org/project/advancedqueue
It's the Queue API rewrite that we needed to do for our needs in Commerce.
Comment #16
cliddell commentedI can second @malcom_p's use case. I recently ran into the same issue of needing to extend DatabaseQueue for a module I'm currently writing, but being unable use the queue service in the module to make use of it.
Comment #21
volegerIntroduced the first version of the refactoring. Reviews are welcome.
Comment #25
volegerChanging target branch to 10.0.x.
Comment #26
andypostI'm gonna remove settings service from arguments in #3299828: Stop storing Settings singleton in object properties
Comment #27
volegerRerolled against 10.1.x
Comment #28
smustgrave commentedMoving to NW for the open threads and suggestions made.
Comment #29
volegerNeeds review
Comment #30
smustgrave commentedThis issue is being reviewed by the kind folks in Slack, #needs-review-queue-initiative. We are working to keep the size of Needs Review queue [2700+ issues] to around 400 (1 month or less), following Review a patch or merge request as a guide.
Code wise everything looks good
New functions and properties are typehinted
trigger_errors are call where needed
Only issue I see is that the link the trigger_error calls should point a change record that shuld be added to this ticket.
Other then that everything looks good
Good work!
Comment #31
volegerAdded CR https://www.drupal.org/node/3338022 , updated links in PR
Comment #32
smustgrave commentedThanks! Can you also include in the CR that the QueueFactory now requires QueueWorkerManagerInterface.
Would search for other CR where parameters are added for wording suggestions.
Comment #33
volegerUpdated CR.
Comment #34
smustgrave commentedThink that should be good.
Comment #35
catchLeft some comments on the MR.
Comment #37
solideogloria commentedThe issue summary mentions an annotation class, but that's not how the merge request is solving the problem. Is there a way to use an annotation? Or should the solution continue down the path of a member variable/constant/function?
Comment #38
berdirThe definition is the annotation and the MR updates the annotation.
This would need to update the attribute class now as well.
I'm not really convinced this is a good idea. I see that there are sometimes use cases where you implement a custom queue plugin and want to use a specific storage at the same time and that currently requires an extra line in settings.php, but I'd argue it's at least as common to use a certain backend for a queue plugin that you didn't implement yourself and this now requires a module with custom code, that might need its own set of environment-specific control to only do that on certain environments. Maybe we support both without deprecating the existing approach, not really a reason to not support both? The cache backend is similar in that cache bins can have a default backend that can be overridden.
Comment #39
volegerSettings.php overrides are unsuitable for the modules that try to control the backend of the queue worker.
I like the idea of keeping the current approach to override the backend service.
What if, instead of extending attribute/annotation, we introduce an extension point by dispatching an event right after determining the service name?
Comment #40
volegerUpdated CR
Comment #41
berdirI still have reservations about this as I said in #38. IMHO, this should only add a new way to configure this and not deprecate the existing settings key.
I understand that there is a use case that specific queues want to default to a specific backend. But it's also a site/infrastructure level question, even for specific bins to move them to a different backend.
IMHO, this should mirror the logic in \Drupal\Core\Cache\CacheFactory::get:
bin/queue setting > bin/queue specific default > global setting > database fallback.
Comment #42
smustgrave commented@berdir thoughts on the latest changes?
Comment #43
ghost of drupal pastI still do not understand the need from the IS:
The code to place an item surely uses a specific queue name and does not look it up based on the queue worker, does it? If that's so then decorate the
queueservice and return your own backend based on the queue name. The responsibility of the worker is to process an item, why does it care where it came from?Comment #44
volegerAt the moment, there is no way to provide a different backend for a specific queue other than setting it in settings.php. I attempted to implement the UI for such a configuration, but ended up with no way to alter those values in the dedicated module implementation. QueueWorkerManager aggregates definitions and also allows definition alteration in info hook implementations. I thought the QueueWorker definition could provide a way to switch the selected backend if a different backend is not set for the queue in settings.php.
Comment #45
ghost of drupal pastThis is true. I recommend using a
queueservice decorator for the purpose. Is that not adequate?Comment #46
volegerYes, it is an option. There is a Queue Order project that provides decoration for QueueWorkerManager for a different reason, so I think decorating QueueFactory should do the backend override.
Comment #48
smustgrave commentedMost likely will need test coverage too.