Problem/Motivation

An entity's update and insert hooks are used to trigger effects once an entity is saved. However at this point the entity has not actually being committed to the database. See \Drupal\Core\Entity\Sql\SqlContentEntityStorage::save().

This causes interesting problems for modules that

  • Having long running tasks as a result of an entity save
  • Resave the same entity - for example the Lingotek module

Proposed resolution

Consider using queues to allow actions after an entity save to occur not in the same database transaction or even the same request. By default we could process x number of items on this queue so that the effects of the entity save don't have to wait until cron runs.

Remaining tasks

User interface changes

tbd

API changes

tbd

Data model changes

None

Release notes snippet

Comments

alexpott created an issue. See original summary.

bojanz’s picture

The main blocker here is introducing some kind of a post save hook, that would run outside of the transaction.

This has been discussed in several issues over the years. For example:
#2892654: Entity save post transactional hook
#2992426: Add entity methods and hooks for executing pre/post code outside of the save transaction

catch’s picture

Some modules (usually dealing with notifications) already have their own queue handling, but there is quite a lot of boilerplate that goes into implementing hook_*_insert()/ hook_*_update() to create the queue item then defining the queue itself.

I think we could look at the queue itself invoking a post-save hook so that modules can simply implement that hook to do what they need to do.

hchonov’s picture

I think we could look at the queue itself invoking a post-save hook so that modules can simply implement that hook to do what they need to do.

This still requires a post transactional hook as pointed out in #2, but will not be able to cover the second requirement from the issue summary:

allow actions after an entity save to occur not in the same database transaction or even the same request.

catch’s picture

@hchonov why doesn't it cover the second point? The queue runner will be outside the transaction, and (usually) outside the request.

hchonov’s picture

@hchonov why doesn't it cover the second point? The queue runner will be outside the transaction, and (usually) outside the request.

Because you've mentioned post-save I thought that you want to do that as part of the save process. But I guess I got that wrong.

catch’s picture

Ahh OK so what I am thinking of is something like this:

1. Every entity type gets an post-save (maybe after-save queue is better in case we ever add post-save hooks?), we always create a queue entry and run it (maybe only if there are hook implementations).

2. The queue runner invokes a post save / after save hook, which is documented as being run via a queue. Modules can then implement that hook, without having to implement their own queue.

Tricky issues:

1. What happens if an entity is saved twice and two queue items are created before either of them runs?

2. What happens if an entity is saved, creating a queue item, then deleted before the queue item is processed?

I doubt we want to save the entity object in the queue item, would think just ID and load it in the runner.

hchonov’s picture

1. What happens if an entity is saved twice and two queue items are created before either of them runs?

Shouldn't there be only one item for an entity? Also it is possible to re-save an entity during the invocation of the entity hooks - I think that this definitely should not create two items.

2. What happens if an entity is saved, creating a queue item, then deleted before the queue item is processed?

I would block the entity deletion until the queue is fully processed.

I doubt we want to save the entity object in the queue item, would think just ID and load it in the runner.

This makes sense only if there is one item for an entity, otherwise one might be interested in the made changes.

catch’s picture

We don't have an API for unique queue items, although there's an issue at #1548286: API for handling unique queue items. That might help for the double save issue although I'm not sure it's a blocker.

Similarly there's no way to introspect queues to see what's in them - because we allow queue implementations that don't allow such introspection, preventing deletion doesn't seem like a good option to me just because a queue hasn't run yet.

hchonov’s picture

preventing deletion doesn't seem like a good option to me just because a queue hasn't run yet.

I can see why one would not like such thing, but one is for sure - we cannot delete the queue item(s) for the entity, as it might be necessary to perform a certain action after the entity is saved. And performing actions for an entity which is already deleted is kind of strange and might lead to all kind of errors if the process might need to load previous revisions of the entity.
What about introducing a deleted flag/field and let the entity queries ignore deleted entities? After all the queue items are processed we can purge the entity.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

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.