Closed (fixed)
Project:
Entity Reference Revisions
Version:
8.x-1.x-dev
Component:
Code
Priority:
Major
Category:
Feature request
Assigned:
Unassigned
Reporter:
Created:
20 Sep 2023 at 12:04 UTC
Updated:
1 Jan 2026 at 13:04 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #2
solideogloria commentedComment #3
berdirA test would be useful to demonstrate this, but looks sensible and simple enough.
Comment #6
mably commentedI created an MR based on Remco Hoeneveld patch allowing to delete orphaned targeted entities when a specific revision entity having some composite fields is deleted.
Our use case: a multi-level hierarchy of paragraphs, some paragraphs containing other paragraphs, etc.
When deleting a specific node revision containing several imbricated paragraphs revisions, those paragraphs revision entities are never automatically deleted if the patch is not applied. You need to run the orphan purger batch multiple times depending on the number of level you have in your hierarchy of paragraphs.
For those interested, here is a custom presave entity hook we implemented to add "removed" paragraphs entities to the orphan purger queue to cleanup all the new potential orphaned entities:
It works perfectly fine and allows us to keep our paragraph revisions entities volume under control without to have to run the manual batch tool periodically.
I think it should get merged.
Comment #7
mably commentedComment #8
mably commentedUpdated patch from MR to fix small typo in function name:
refrence->reference.Comment #9
berdirInstead of a a prefixed "internal" function, what if we already convert this to the new #Hook + #LegacyHook hook syntax? A single method can then implement both hooks on 11.1+ and the two old hooks can be marked as #LegacyHook. That's fully forward compatible and will not need to be touched anymore until D10 support is dropped.
https://www.drupal.org/node/3442349
Comment #11
mably commentedTried something with MR 62.
Comment #12
berdirNice, reviewed.
Comment #13
berdirThanks for the quick turnaround. Tests would be nice, but I'm OK with merging this as it is now.
Comment #15
berdirComment #18
berdirNote: \Drupal\entity_reference_revisions\Plugin\Field\FieldType\EntityReferenceRevisionsItem::deleteRevision() already exists, so deleting a revision should in most cases directly delete that revision, so this will add considerably extra load on deleting revisions as it will expand the queue :-/
Comment #19
berdirReopening this for now, considering to revert this because this is a considerable overhead when doing everything twice. I'd like to understand the scenario when this is needed better. One option would be to remove revisionDelete().
Comment #20
mably commentedHi @berdir, what do you mean exactly by "doing everything twice"?
Comment #21
berdirI mean that we'd immediately delete the revision but then also register a queue item to check whether or not we should delete that revision.
So we need to figure out when and why deleteRevision() doesn't do its job, a test for that would be very helpful.
The reason I commented and re-opened is that I noticed today on a client project that a node had 5000 revisions and node_revision_delete was somehow not running properly. So I did run the manual batch, and then, knowing about this issue, did run the manual ERR orphan purger. But it barely did anything and upon closer inspection I realized that basically all the revisions already had been deleted. So it was working as it should, for me anyway.
Comment #22
mably commentedOur use case is to be able to properly delete a specific revision of a node, not the whole node, without having to run the orphan purger.
The problem might be that be that we add several times the same entity to the orphan purger queue, possibly many times if we have a lot of revisions and we delete the node entirely.
Could it be possible to store in a request cache the entities already added to the orphan purger queue so we don't add them several times?
Comment #24
mably commentedOk, I think I see what you mean when you say that everything is done twice.
When running the orphan purger batch we don't need to add the processed entities to the purger queue.
Is there a way to know that the hook are being called from the batch processing?
If so, we could just skip the processing in such a situation.
Anyway, I tried to work on a static cache to avoid processing multiple times the same entity in the same request. It seems to avoid a lot of unneeded revision hook processing when running the orphan purger batch with a lot of revisions.
You can check the MR added to this issue.
Comment #25
liam morlandThe change in this issue has caused #3512911: Make Composer version requirements match the info file.
Comment #27
berdirI realize this is awkward after so long, but there hasn't been a release and I'd like to better understand what problem this actually solves, so I decided to revert this. What I'd like to see per #21 is a test that fails by pointing out a revision that has not been deleted as expected without this.
Comment #29
mably commentedHi @berdir, could you have a look at my test in MR 76?
Looks like I was able to reproduce the bug we are encountering when deleting nested revisions.
EDIT: Actually the test is probably bogus as it still fails even with this issue's patch applied. Investigating.
EDIT2: In fact the test passes fine if you run the purger batch a second time as described in #6.
Comment #30
mably commentedI finally wrote two tests (MR 76):
@berdir is there any thing we should add?
Comment #31
berdirThanks for sticking with this. Added some comments but didn't fully review the tests yet. You can include the fixes too, MR's have the test only job that should allow us to verify the test fails without the changes. And we can possibly also extend the test to ensure that we're not doing more queues than we have to on top of the fixes.
Comment #33
mably commentedRe-added the fixes in MR 76.
"Test only" job is now failing as expected.
Added a static cache in the OrphanPurger queue worker to ensure that we will not reprocess already deleted entities.
BTW the
EntityReferenceRevisionsOrphanRemovalTestseems to be intermittently failing on the "previous minor" test environment. Test is expecting 8 deleted revisions, we are sometimes getting 7.Comment #34
berdirThanks for sticking with this. I've updated the comment on why those revisions of node rev2 aren't deleted (It's because those are the default revisions, so more checks are needed to verify that all revisions of the reference are unused, in case there are any.
This made me realize what I think the issue here is that you're seeing. Exactly that default revision check. I think we can do this more efficiently and avoid those extra queries on revision deletions by instead adding them to the queue in that case. With that change, your tests are passing for me without the revision delete hook.
Comment #35
berdirI'd like to commit this, but the test seems to consistently fail on next minor which I can't reproduce locally and also possibly seems to make random fails happen more frequently.
Comment #36
berdirI can reproduce the test fails now and it's pretty complicated. It happens in the setup when the number of revisions created diverge in 11.3 compared to 11.2.
On line 248, instead of 3/3 we now have 3/4 revisions, because B also gets an new revision through A when the node is saved. This might be a bug in core. It has to be related to entity revision cache, but I couldn't exactly pinpoint the change and reason yet.
But what's essentially happens is that when A is saved, and its field_composite_entity field is checked for hasTranslations, which then goes to \Drupal\entity_reference_revisions\EntityReferenceRevisionsFieldItemList::hasAffectingChanges, which checks if the target has affected changes. Because the target (composite B) isn't being saved (yet), \Drupal\Core\Entity\ContentEntityBase::hasTranslationChanges() has no preloaded original. Because a new revision for composite B was just created, but the revision referenced by this field *was* a default revision, it's comparing it against the new default revision, sees a change and then decides to resave the referenced revision.
This doesn't add up of course, since it's comparing it against the wrong version. I'm not entirely certain what we should do in this case and if core is wrong (and it should always load a revision) or if ERR should set the original in this scenario since we're explicitly checking changes on an old revision. These checks are expensive, and it again comes down to missing change tracking of a loaded entity. It's way too late to continue on this, just writing down my findings.
Comment #37
mably commentedThat's some amazing investigation work, thanks @berdir.
Comment #38
berdirWorked around the problem with hasTranslationChanges(), tests are now green on all core versions. Opened #3562759: ContentEntityBase::hasTranslationChanges() must use loadRevisionUnchanged()
Comment #39
berdirThe move of the hook is a bit out of scope now as we're not actually changing that, but we need to do it eventually.
Merged this again, lets hope it sticks this time!
Comment #41
berdirI forgot about the random fails and the test change there.
Comment #43
berdirHad two random fails, but also had thata random fail on the last two weekly pipelines.
Comment #45
mably commentedThat was a tricky one. Thanks @berdir!
Comment #46
boulaffasae commentedHi everyone,
I ran into an issue after updating entity_reference_revisions to the latest version.
Error: Call to a member function getTranslation() on null in Drupal\entity_reference_revisions\EntityReferenceRevisionsFieldItemList->hasAffectingChanges() (line 188 of /app/web/modules/contrib/entity_reference_revisions/src/EntityReferenceRevisionsFieldItemList.php).The following line seems to be the cause of the issue:
In this context, $this->entity can be NULL. Since the code is iterating over referenced entities, it seems that $item->entity should be used instead.
Current code:
Comment #48
berdirThank you, that a very awkward mistake. Could you open a new issue so we can commit it with proper context and contribution credits?
Comment #49
boulaffasae commentedDone #3563886