Problem/Motivation

When executing a bulk operation on a view with exposed filters, the confirmation form (and action processing) loads and processes all entities matching the base view query and not just the filtered subset visible to the user. This causes extremely long response times on views with large result sets.

The root cause appears to be that ViewsBulkOperationsActionProcessor::populateQueue() is removing the exposed filters. It re-executes the view without applying the active exposed filter values, effectively iterating over the entire unfiltered dataset.

Steps to reproduce

  1. Create a view with a large number of results (e.g. 10,000+ entities).
  2. Add exposed filters that significantly reduce the visible result set (e.g. to ~50 items).
  3. Add a VBO field with a custom or core action that uses a confirmation step.
  4. Apply the exposed filters so only a small subset is displayed.
  5. Select one or more items and execute the action.
  6. Observe that the confirmation form takes an unreasonably long time to load - response times scale with the total unfiltered result count, not the filtered subset.

Actual behavior: The confirmation form iterates over all entities in the unfiltered view (thousands), causing long response time.

Expected behavior: Only the entities matching the currently active exposed filters should be processed. Response should be quite quick.

Workaround: Replacing exposed filters with non-exposed (contextual or hardcoded) filters resolves the performance issue, as the base query itself then only returns the relevant subset. This is not a viable long-term solution for views that require user-facing filter controls.

Possibly related issues:

  • #3591877: Out of memory when building confirmation form -> likely the same root cause manifesting as OOM instead of long response times.
  • #3530311: Add additional sort by langcode in getPageList() -> different issue (translation handling), but touches the same code path in ViewsBulkOperationsActionProcessor.

Some further notes

In populateQueue() there is this comment to describe why the exposed filters are ignored:

Remove all exposed filters so we don't have any default filter values that could make the actual selection out of range.

Not sure, but the function seems only be used to display the labels of selected items.

Proposed resolution

tbd

Remaining tasks

  • Confirm the exact location where exposed filter values are lost during the confirmation form build.
  • Implement a fix that persists and re-applies exposed filter input in the action processor.
  • Add test coverage for bulk operations on views with active exposed filters.
  • Verify fix against large datasets (10k+ entities) to confirm performance improvement.

User interface changes

None expected. The confirmation form should behave identically from the user's perspective. It will simply load faster and process only the correct subset of entities.

API changes

None expected.

Data model changes

None expected.

Command icon 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

pminf created an issue. See original summary.

pminf’s picture

Issue summary: View changes
lykyd’s picture

I arrived on this issue after noticing that my bulk operations had a massive performance loss since I moved from Drupal 10 to 11 (but the reason was the update of VBO I had to perform alongside). I'm not sure if it should a separate issue referencing this one.

By making this fix : https://www.drupal.org/project/views_bulk_operations/issues/3591877, we ignored the fact that the count query doesn't retrieve a limited number of items based on the condition.
So the display of the confirmation form generates a query on possibly thousands on items, instead of the real number of selected items to apply the operation to.
Also, it might impact the value of `total_results` of VBO.

In my tests, setting the `count_query` seems to be fixing this behaviour but I could not dive deep into it for now :

    $built_query = $this->view->build_info['query'];
    if ($built_query instanceof SelectInterface) {
      $built_query->condition($base_field_alias, $base_field_values, 'IN');
    }

    $count_query = $this->view->build_info['count_query'] ?? NULL;
    if ($count_query instanceof SelectInterface) {
      $count_query->condition($base_field_alias, $base_field_values, 'IN');
    }

graber made their first commit to this issue’s fork.

graber’s picture

Priority: Normal » Major
Status: Active » Needs review

graber’s picture

Version: 4.4.7 » 4.4.x-dev
Status: Needs review » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.