Problem/Motivation
Views forms (VBO, for example on /admin/content
) mark forms as cacheable.
Its done that way in order to ensure that the order executed on the result is the same as when you hit the VBO submit button,
so you can ensure that the selected rows match the actual rows seen by the other.
Proposed resolution
- Instead of using the row key, create a new method which calculates a unique identified for each row.
- Use that identifier when the form is built, so we don't rely on the order anymore
- Disable form caching for those forms
Remaining tasks
None
User interface changes
None.
API changes
No API change
Comment | File | Size | Author |
---|---|---|---|
#42 | interdiff.txt | 1.15 KB | Gábor Hojtsy |
#42 | 2486433-42.patch | 9.34 KB | Gábor Hojtsy |
#39 | 2486433-39.patch | 9.32 KB | Gábor Hojtsy |
#39 | interdiff.txt | 2.18 KB | Gábor Hojtsy |
#34 | 2486433-34.patch | 8.92 KB | damiankloip |
Comments
Comment #1
Wim LeersComment #2
tim.plunkettHonestly I would assume this to be an indication of insufficient test coverage more than anything
Comment #3
Wim LeersCan you explain for what this exactly necessary then? i.e. what should the test coverage cover?
Comment #4
tim.plunkett#1473276: Views Form is not cached, can lead to data loss and corruption added this.
#1473276-5: Views Form is not cached, can lead to data loss and corruption is amazing. Per @dawehner:
Comment #5
catchIt looks like we'll need different approaches for various types of form to solve #2263569: Bypass form caching by default for forms using #ajax.. Bumping this to critical, but also tagging 'Needs issue summary update'.
Comment #6
plach@Wim:
I'm available to work on this if you need to focus on something else, but I'll need directions (updated IS at least) and early reviews :)
Comment #7
effulgentsia CreditAttribution: effulgentsia at Acquia commentedUpdated IS would be good, but in a nutshell, see #4. I think the issue is that the row checkboxes for VBO are keyed by row number, so if the view in $form_state isn't persisted, then it is rerun on submission and the row number of a given entity can change (e.g., if an entity was added or removed in the meantime), and you'd operate on the wrong entity. The fix is probably to key the checkboxes on entity id or uuid. But, how to handle Views that display non-entities, such as watchdog entries?
Comment #8
damiankloip CreditAttribution: damiankloip commentedThat ship also sailed with #2252763: Views exposed filter form causes enormous form state cache entries as that rebuilds things anyway so we do not serialize all the data. That makes turning this off almost moot from that point of view.
Let me take a look at what we did there and report back here. I think that would help quite a bit.
Comment #9
Fabianx CreditAttribution: Fabianx as a volunteer commented#7: Quite easy, we now _have_ a permanent and unique row identifier:
Use the new cache keys from the views row caching instead of the Row ID => done :).
Comment #10
Wim Leers#6/@plach: I'm not working on this at all. Feel free to take it on :)
#8: Awesome, thank you so much! :)
#9: oh heh…
Comment #11
damiankloip CreditAttribution: damiankloip commented#9, I am not sure we can use that, as recalculating it would result in a new ID anyway? And we also can't rely on loading the cached row to get what we need.
Comment #12
dawehnerTalked with damian about it and we thought about simply using entity ID + revisions ID + langcode, given that the views bulk operations are based upon entities only anyway.
Comment #13
damiankloip CreditAttribution: damiankloip commentedThis is a rough first patch, but I think we should do something like this:
- Generate our own return values on the bulk op checkboxes containing the langcode, ID, and VID if applicable. This will be a string with these values in
- Use this key instead to load the entity that we should load and action
- The rest can remain unchanged as we only need the change the mechanism to load entities we want to perform bulk actions on
The methods to create the IDs are not ideal, but it will be messy either way unless we can call out to the entity itself to get this. Entities already have a shit load of stuff on anyway so this would be a drop in the ocean I guess... :)
Comment #14
dawehnerIs there any risk in using "-" as separator?
What about adding a @return :P
Comment #15
Fabianx CreditAttribution: Fabianx as a volunteer commented#11: Yes, we can, the row-cache-id is unique by design (else caching would not work).
However entity_type, entity_id, revision_id, langcode works for me.
Edit: #13 looks great to me!
Comment #16
dawehnerAt least for views we don't need the entity type but I agree for a general functionality, we should add it.
Comment #17
catchNot sure that will work.
If I submit a bulk delete operation, entity id 2 has vid 2.
Someone updates entity ID 2, so it moves to revision id 3.
Now the ID won't match.
But is there any need to have the revision ID? Can't update revisions in place as such.
edit: except for deleting revisions..
Comment #18
damiankloip CreditAttribution: damiankloip commented#15, Glad that approach seems feasible :)
I can add the entity_type to the new bulk key, we don't really need it though. Do you see another use for that?
row-cache-id would not work, as it uses the row index to create the ID. So that would essentially be the same as what we have now. The order of your view could change, and you would not be able to load the item anyway. Aside from that, we should not use caching based code here IMO.
Comment #19
damiankloip CreditAttribution: damiankloip commentedComment #20
dawehnerI see a usecase for it in case we would have a generic functionality which would be part of the EM.
For a quick moment I thought
\Drupal\Core\Entity\Entity::getConfigTarget
could be useful, but it turns out, not at all.Comment #22
damiankloip CreditAttribution: damiankloip commentedFixed the tests and added a couple of quick docblocks. So questions:
- Do we want to add the entity_type to the key? We always know the entity type form the bulk form
- Not sure if there is a cleaner way to implement the id and/or the loading of the entity from the ID parts?
Also added back Wim's patch from #1.
Comment #23
Fabianx CreditAttribution: Fabianx as a volunteer commented#22: The clearest way would be to have a EntityLoaderSerializer() or such that given data of an entity can ensure it can be loaded again.
That would be useful in many other parts of core. However I think its overkill for here and a simple delimiter based format works fine.
Is security a concern - that we need to - expose the IDs to change to the user?
e.g. Do we sufficiently check that I cannot just change the user-input key to some entity id I do not have access to?
Comment #24
damiankloip CreditAttribution: damiankloip commented#23, Access is already checked in the code from before:
But maybe we should hash the checkbox return_values?
Comment #25
Wim Leers#22: Awesome! Patch is looking better and better :)
Comment #26
almaudoh CreditAttribution: almaudoh commentedDid you mean
loadEntity*From*BulkFormKey()
?Comment #27
damiankloip CreditAttribution: damiankloip commentedCertainly did! :)
Comment #28
damiankloip CreditAttribution: damiankloip commentedComment #29
Wim LeersCan't wait to see @dawehner/@plach reviews of this and get this in :)
Comment #30
dawehnerdo you mind using quickly
Fabianx asked for a more generic approach. I think we should not try to tackle that as part of this issue. I remember the following issue: #1983100: Provide a LoadableInterface for Typed Data objects and its endless discussions. Let's be pragmatic and fix this issue. If we want to have a generic implementation, that should be its own thing, especially because we already have some serialization format for config dependencies.
nitpick NR2: Can we have a bit more documentation? Sorry. We should explain why we have such a method
Comment #31
dawehnerIn general though I really like the patch as it is!
Comment #32
plachCan we change the signature not be entity-dependent? E.g.:
This way if in the future we want to implement VBO also for for non-entity stuff we are not forced to introduce BC layers and pollute the public (protected :) API.
Same here, can we remove "entity" from the method name and return a row?
Other than that this is looking good to me.
Comment #33
dawehnerYeah doesn't looks like a totally bad idea! Here is an issue summary.
Updated the issue summary.
Comment #34
damiankloip CreditAttribution: damiankloip commentedSo, a complete rewrite of the patch then... :)
#30.1 - Every other expectation in both of those tests uses "->will($this->returnValue($entity_storage))", so this seems more consistent for now.
#30.2 - This seems to go against your comment in #33, or you mean this issue should not tackle an EntityLoaderSerializer approach?
#30.3 - Added some docs
I am also not sure about test coverage, we have bulk form test coverage, and probably trying to replicate stuff changing between a form being loaded and a form being submitted my be tricky? We could maybe unit test the bulk form class.
#32.1, #32.2: Hmm, I am not sure actually. BulkForm is completely coupled to entities anyway, so I don't see any gain from this? Also, other problems with that approach are: If you create a unique ID for a row, similar to the row cache ID, you need to find it again. So you need to execute the view on submission, and then calculate this value for each row again to find your entity. But then, once you do this, you are still not guaranteed that the entity you wanted to act on is in the re executed view. So that is a similar problem as we already have now. Thoughts?
Here is the doc update for now.
Comment #35
dawehnerWe talked about that part. While writing a method with a generic signature for the $row =>ID part is pretty straightforward, its entirely not fair to do the loading side. You can't pass in arbitrary things, but its just bound onto entities for now. You know, in case we have a better idea in the future, we can just adapt the internal implementation ... this is why we have a protected method here.
Yeah I think we totally should not. If someone cares, feel free to open up a dedicated issue for that.
The issue summary is now up to date, we don't need test coverage. Sheep it
Comment #36
jibranNice work seems very elegant solution. RTBC++
#1986606: Convert the comments administration screen to a view would need a re-roll after that. It would be great if we could have change notice for this. A lot of contrib module can inherit BulkForm plugin.
Comment #37
alexpottEntityStorageInterface and LanguageInterface are now not used.
allows
Needs a description
This needs to be fully namespaced for api.d.o
Missing @param doc
@param and @return both need descriptions.
Comment #38
Gábor HojtsyWorking on those now. Also marking for D8MI.
Comment #39
Gábor HojtsyI think this addresses all @alexpott's concerns from #37.
Comment #40
Gábor HojtsyComment #41
dawehnerLet's use a FQCN
Comment #42
Gábor HojtsySure. Also found another docs typo.
Comment #43
dawehnerThank you! Back to RTBC
Comment #44
alexpottThis issue addresses a critical task and is allowed per https://www.drupal.org/core/beta-changes. Committed 6eb89b5 and pushed to 8.0.x. Thanks!
Comment #46
Gábor HojtsyAmazing. Let's add test coverage for the multilingual improvement that this issue did in #2484037: Make Views bulk operations entity translation aware.
Comment #47
Fabianx CreditAttribution: Fabianx as a volunteer commentedRTBC + 1 - extremely efficient patch for a difficult problem, congrats Damian!