Problem/Motivation

When a batch is submitted, the batch runner completely takes over the user interface, preventing any other operations. An example of this is when you install Umami via the installer UI, and the config translation sync runs after submitting the site information form, taking an extra 15-30 seconds to get the site installed.

Instead, if we have the information required already, we could add a batch runner to the site information form page itself, then config translations would download while you're filling out the form. It might still need to block submitting the form until this completes, but it would probably be done by the time you get to the end anyway.

Steps to reproduce

Proposed resolution

This needs concepts and capabilities which currently don't exist in the batch API, we'll need to decide whether we try to extend the existing API or make this part of an overall task to replace it.

The user-facing part is a batch runner element, which can display a progress bar, and fires off AJAX requests to a batch callback (possibly system.batch_page.json, possibly a new one), which can be used to run a batch instead of having to redirect to /batch and then redirect away from it again or using _batch_page() directly as the installer does.

The js supporting this batch element might need to prevent link clicking and form submits, or maybe we can do a similar thing to the status messages system, and continue the batch AJAX running on the next page if there is a batch in $_SESSION. This would allow batch operations to be completely non-blocking and complete in the background (still with a progress status shown to the user).

It would also be useful if it was possible to tell the batch runner to run a queue API queue down to zero - this might let us move some long running tasks into queues - then sometimes you might want a progress bar to run the queue down fast, and sometimes you might want to let cron handle it, but without having to do two or more implementations. Most things which are re-entrant and possible to trigger again from the UI could move to queues then. This could be implemented as a generic batch that takes a queue name probably.

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

CommentFileSizeAuthor
#14 operation-example.png53.87 KBchi

Comments

catch created an issue. See original summary.

catch’s picture

gábor hojtsy’s picture

IMHO this sounds fantastic. For its use in the installer, but also in other cases, I think its important that it actually runs through, which is what may be hard to ensure. I guess you can already leave the page when a batch is running but now that it would be less evidently running, people may leave the page even more. That said, it would be fantastic if we would not block the user doing something just because we also need Drupal to do a lot of things in the background.

catch’s picture

I guess you can already leave the page when a batch is running but now that it would be less evidently running, people may leave the page even more. That said, it would be fantastic if we would not block the user doing something just because we also need Drupal to do a lot of things in the background.

Yes this is going to be one of the harder pieces. We can probably cover a lot if we keep track in the user's session that they started a batch (probably the batch ID), check the session on every page request to see if we need to show the batch element, and then the element resumes where it left off if there's a batch still running. Then finishing a batch needs to clear $_SESSION['active_batches'] or similar.

gábor hojtsy’s picture

Also the tricky part about this may be that the installer language import updates default config en masse so we nee to be careful what other config saves we allow at the same time as they may go over each other :)

catch’s picture

A possible approach would be that when the batch is running, something like installing a new module via project browser would have to add a new item to the end of the batch instead of doing it immediately. Or a less complex implementation we could disable form submits while the batch is running (but not filling out form fields or clicking around the project browser UI for example).

I also think that, separately from this issue, we could add some parallelism to the language import batch via Fibers and guzzle async requests - like fire off 5 or 10 translation requests at a time per batch iteration. That could probably be done self-contained in its own issue, would make the current blocking batch a lot quicker, and mean much shorter windows for things to potentially go wrong or form submits to be prevented for this issue.

gábor hojtsy’s picture

Yeah downloading the translation files could be very well parallelized, if d.o file server can handle the load :) Importing them I'm not sure can be parallelized since importing a .po file can have far reaching effects on dozens of config files at once. Then the importing next .po file is has effects on another dozen files, some overlap with the previous, etc.

catch’s picture

Importing them I'm not sure can be parallelized since importing a .po file can have far reaching effects on dozens of config files at once. Then the importing next .po file is has effects on another dozen files, some overlap with the previous, etc.

Yeah importing we can't really do async with other imports, but we can do it once we've downloaded one file and are waiting for other ones to finish.

So instead of:

ABABABABABAB

It's more like:


ABBBBBB
AAA
A
A

Opened #3515390: Parallelize contrib translation downloads.

chi’s picture

I did some experiments with running queues through JS polling.
https://www.drupal.org/project/queue_manager

The module handles pure queues not batch operations but the approach is similar to what has been discussed above.

The queue status is just a render element that constantly gets queue size through ajax requests.

/**
 * Provides a render element to get queue size.
 *
 * Properties:
 * - #title: Element title.
 * - #queue_name: Queue name.
 * - #refresh: Refresh timeout in seconds.
 *
 * Usage Example:
 * @code
 * $build['example_status'] = [
 *   '#type' => 'queue_size',
 *   '#title' => 'Number of items',
 *   '#queue_name' => 'example',
 *   'refresh' => 1,
 * ];
 * @endcode
 */

And for running the queue there is another render element.

/**
 * Provides a render element to run a queue.
 *
 * Properties:
 * - #queue_name: Queue name to run.
 *
 * Usage Example:
 *
 * @code
 * $build['example_runner'] = [
 *   '#type' => 'queue_runner,
 *   '#queue_name' => 'example',
 * ];
 * @endcode
 */

For the Batch API we can create a similar couple of elements. BatchStatus (progress par) and BatchRunner. It would give us the following features that are not available in the current Batch API implementation.

1. Not blocking progress bar. It would be just an ordinary render element. It can emit some JS events when the batch is finished or failed allowing to block/unblock form submission.
2. Multiple progress bars for different batches on the same form.
3. Concurrency. In the above example if you add another queue_runner element the queue will be processed in two threads.

andypost’s picture

Generally speaking not every queue system has ability to get know a length of the queue (MQTT), moreover some of them give very approximate numbers (SQS/KAfka) so sometimes you need observability system to get this data

andypost’s picture

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.

chi’s picture

chi’s picture

StatusFileSize
new53.87 KB

I created a module that allows to run tasks and display the operation status in non blocking mode. The approach is similar to what was described in #9. The progress bar is just a read-only indicator of the operation status. The queue runner is fully decoupled from page state.
The modules is not battle tested yet. The current development status is "working PoC".

Operation Example

chi’s picture