Problem/Motivation
We decided in #2976035-13: Entity type CRUD operations must use the last installed entity type and field storage definitions to drop support for automatic entity updates, for the many reasons stated in that comment.
However, we can add support for changing the entity schema to (non-)revisionable/(non-)translatable by using "manual" entity updates (i.e. explicit update functions), which should work for entity type with and without data.
Proposed resolution
Generalize and improve the code in \Drupal\Core\Entity\Sql\SqlContentEntityStorageSchemaConverter
to not use the live entity type and field storage definitions anymore, and make it work as part of SqlContentEntityStorageSchema
instead of trying to work around/against it.
Remaining tasks
- Evaluate concerns espressed at #53.
- Review/commit.
User interface changes
Nope.
API changes
API addition: a new updateEntityTypeSchema(EntityTypeInterface $entity_type, array $field_storage_definitions, array &$sandbox = NULL)
method added to EntityDefinitionUpdateManager
.
Data model changes
Nope.
Release notes snippet
Starting with Drupal 8.7.0, the Entity system provides a new Update API for converting the schema of a content entity type from (non-)revisionable/(non-)translatable to revisionable/translatable, which also works when there is pre-existing data for the entity type whose schema is being changed.
Comment | File | Size | Author |
---|---|---|---|
#67 | interdiff-67.txt | 7.43 KB | amateescu |
#67 | 2984782-67.patch | 73.27 KB | amateescu |
Comments
Comment #2
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedHere's an initial patch with how I think this new API could look like.
It doesn't yet do anything, we still need to bring over and improve the code from
SqlContentEntityStorageSchemaConverter
, but I'd like to get some opinions on the general proposed approach :)Comment #3
Manuel Garcia CreditAttribution: Manuel Garcia as a volunteer and at Appnovation for Pfizer, Inc. commented+1 I think this is a great idea!
Since this is an API addition, we'll need to add test coverage I assume?
Comment #4
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedMy initial idea was to convert all the tests that are being made obsolete in #2976035: Entity type CRUD operations must use the last installed entity type and field storage definitions to use this new API instead :)
Comment #6
plachThis depends on #2960136: Add the ability to use a prefix for all the tables in a table mapping.
Comment #7
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedHere's an implementation for this new API that's almost complete. The data copying step needs to be improved to handle all conversion cases, right now it can only handle the conversions from
non_rev_non_mul
torev_non_mul
and torev_mul
, because that's what the existingSqlContentEntityStorageSchemaConverter
class handles.I also updated
SqlContentEntityStorageSchemaConverter
to use the new API, and the passing test coverage proves at least that there's no regression for those two types of conversions.The thing I like most about this interdiff is that the lines-of-code changes are neutral (
5 files changed, 433 insertions(+), 439 deletions(-)
), but the new API should be able to handle all types of conversions easily. I think it's even possible to handle converting from a storage class to a totally different one (e.g. from core's sql storage to a contrib nosql storage) with some minor tweaking, but I don't think this should be in the scope of this issue :)The combined patch includes the one from #2960136-19: Add the ability to use a prefix for all the tables in a table mapping.
Comment #8
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedA couple of updates based on #2960136-21: Add the ability to use a prefix for all the tables in a table mapping and fixed the unit tests.
Comment #9
plachThe blocker is in.
Comment #10
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedI think people can already look at the "for review" patch from #8, so until we have some concrete issues to address, let's try to lure them in :)
Comment #11
jibranThis should be named as
$original_entity_type
everywhere in the patch.Do we want to finish it here or do we need a follow up?
s/Copy data/Copy data from original storage to temp storage/
Do we need a follow-up?
Comment #12
jibranI think we need a change notice here referencing this and #2960136: Add the ability to use a prefix for all the tables in a table mapping because this API can be used to change a string basefield length or convert an int basefield to string.
Comment #13
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedWhat we need right now is architectural review, not nitpicks. Those
@todo
s should be solved here, not in followups.Comment #14
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThis should cover all the possible conversion types. Now we need to test coverage for all of them :)
Comment #15
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedAdded full test coverage for the new API and fixed a ton of things in the process :) This is now ready for final reviews IMO.
Comment #16
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedLet's make this patch a bit smaller by depending on #2624770: Use more specific entity.manager services in core.services.yml and moving the changes to
SqlContentEntityStorageSchemaConverter
into a followup issue: #3004642: Deprecate SqlContentEntityStorageSchemaConverter in favor of the new API provided by EntityTypeListenerInterface::onFieldableEntityTypeUpdate().Comment #18
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedSince we moved the rewrite of
SqlContentEntityStorageSchemaConverter
to a followup, we must keep the temporary stuff inSqlContentEntityStorage
:/ Also rolled back an unnecessary hunk.Comment #19
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedI encountered a problem in #2880149-193: Convert taxonomy terms to be revisionable with tables that are returned by
\Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::getEntitySchema()
and are not part of the default table mapping, liketaxonomy_index
.Comment #20
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedSpotted a small problem with an exception message, this time thanks to #2880152-29: Convert custom menu links to be revisionable.
Comment #21
kristiaanvandeneyndeI love the sanity checks this is running!
I recall not having a data table a long while ago and that was preventing me from adding translations. If I read the tests correctly, this will make it possible for an entity type to go from translatable=FALSE/data_table=NULL to translatable=TRUE/data_table=foobar without data loss? If so, then hot damn I wish you worked on this 2.5 years ago before I wrote group_update_8002
Awesome work!
Comment #22
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThanks, @kristiaanvandeneynde :)
Exactly!
The blocker is in so we can drop the combined patches now. This is just a re-upload of the "for review" patch from #20.
Comment #23
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedComment #24
vasi1186 CreditAttribution: vasi1186 at Amazee Labs commentedThat is some really impressive work!
The code looks good to me, I also did a lot of manual tests to see how is this actually working and the results were good. Except one case (which is also pointed out in the tests): from Revisionable and Not Multilingual to Revisionable and Multilingual. The question is if we should add support for this case in this issue or not.
Comment #25
plachI'm done reviewing #22, tomorrow I'll post what I have. Meanwhile I launched a few additional test runs and it seems we have issues with PHP 5.6 and PgSQL.
Great stuff, btw :)
Comment #26
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThank you both for testing and reviewing the patch :)
@vasi1186, I'm not sure whether we should try to fix that here or in a separate followup issue. The problem is that the entity storage does not allow us to save a (past) revision as-is, so my initial thought is that we could go forward with what we have for now, which allows us to convert taxonomy terms and menu links to revisionable, and fix the tricky revision save issue separately. I'm curious what @plach thinks about this plan though.
@plach, that pgsql fail is a known random one: #2982755: Random failure in SchemaTest::testSchemaChangePrimaryKey with order of composite primary key. And I'm very eager to see your review :D
Comment #27
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThis should fix the problem with php 5.6.
While writing this fix, I realized that the new
EntityTypeSchemaListenerTrait
is not storage-agnostic anymore since can't use an entity query for getting the list of entities to migrate :( Maybe we should move it to theSql
namespace for now and bring it back to the generic one when an entity query is able to use the entity type and field storage definitions from the storage object that instantiated it..Comment #28
plachMost items are nitpicks but I have one serious concern about the current approach, see bullet 7.
When originally designing the entity storage API, we tried hard not to expose the concept of schema in the public API, given that some storage types don't rely on any schema. This is the main reason why the storage schema handler is instantiated by the storage handler. It's true that
EntityDefinitionUpdateManager
internally breaks this attempt, but at least the public API is "clean". Given that the new method is a replacement for::onEntityTypeUpdate()
in disguise (I think we even mentioned the possibility of deprecating the latter in favor of the former), I'm wondering whether we can find a more neutral name, e.g. something like::onEntityTypeUpdateWithData()
. This name might work better even for storage types that don't have a schema to adjust but may decide to store stuff differently, given the updated entity type/field definitions.I didn't realize this was meant to be storage-agnostic, exactly for the reason pointed out in #27. But then I noticed this was (already) in the Sql namespace and I thought that for consistency it would be good to add a
Sql
prefix to the class name.I think it's ok to move it the upper namespace level, once it's actually storage-agnostic, but I think we should mark it as
@internal
for now, if that's the plan.Why would we forbid such changes? Just to prevent unintended data loss? Couldn't we make them available via an opt-in site setting?
I'm wondering whether it would make sense to have empty methods instead. We do this for exception handling. For instance a class might need to implement just one of the two.
Is this a copy/pasted comment? I'm not sure mentioning the previous API makes much sense here.
Wouldn't this be skipped when starting from a rev non_mul or a non_rev mul status? Shouldn't this be:
I know this logic was not introduced here, but this does not seem right to me after looking it with a fresh eye. We are not creating a new entity, we are just storing it again. Making this operation appear as an entity insertion could trigger all sorts of unwanted side effects, e.g. email notifications, statistics updates, integrity constraint violations (see the attached patch for an example), and so on. I really think this outlines the need for a new storage method (e.g
::restore($entity)
), which would internally re-use the::save()
logic, and would internally trigger an insertion if needed, but would appear as an update. Alternative ideas are welcome, but I played with this problem in my mind for a while and couldn't come up with a better suggestion.Aren't we missing a space between ID and revision ID?
It's unfortunate we still have to mess with the internal state to get things right. Is there a plan to improve on this in later iterations? If not, would it make sense to backup/restore the original property values?
I'm not sure this logic is correct: if maintenance mode is off, entities could be added but also deleted, which would mess with the
max
value and potentially make the process never end. I think we should keep track of the latest seen ID and calculate the finished value as:I had to read this code a few times to figure out what "current" was meaning. I'm wondering whether "new" would be easier to understand. Also, I'm wondering whether
tmp_
andold_
are enough as prefixes, since they might clash with existing table names, e.g. manual backups. Couldn't we add a short hash to the prefix, e.g. the first 6 chars of the hash of the serialized table mapping, to reduce the likelihood of conflicts?Can we add a comment to explain why these are needed?
Shouldn't we handle exceptions here and restore the original names if any occurred?
Won't this pollute the stack trace? Wouldn't it be better to wrap it into a new exception?
Could we add a site setting to preserve the backup tables? We could even make it opt-in, just be safe.
Shouldn't we instantiate the storage class from the entity type as we do elsewhere in this patch?
<3
Can we add also some values to test dedicated field storage?
This feels like a logic that regular update function will need to repeat as-is every time. I'm wondering whether it would make sense to add it to the entity type and entity field managers and maybe invoke it automatically from the entity definition update manager.
Can we create a new revision for the translation, just to reinforce good practices? ;)
Argh! Not one but two boolean parameters! :D
This PHP doc is a bit weird: it makes me think that either the method name should be get... or that we should change the PHP doc itself to describe the method's primary intent.
Would it make sense to add an
else
branch asserting that the entity type is neither revisionable nor translatable? We are assuming that no change is performed wrt the initial schema, but we aren't actually testing it AFAICT.Don't these checks belong to
::assertRevisionable()
?@amateescu, #26:
Ideally I'd like to release this API with the ability to support all flavors of "migrations" (including lossy ones, as mentioned above). That said I guess it's ok to fix entity revision in a follow-up, as long as we make sure it's done before 8.7. It would probably be good to have a release manager's opinion on this.
Comment #29
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedAwesome review, as always! :) Let's get the easy stuff out of the way because I need to think through some points in depth in followup comments.
::onEntityTypeUpdateWithData
is a better name for this new API because it implies that it should be used "only" when you have existing data, which is not true at all. Also, would this mean that we should also remove the new interface and add the new method to\Drupal\Core\Entity\EntityTypeListenerInterface
?Sql
prefix and@internal
for now.SqlContentEntityStorageSchemaConverter
. Rewrote it :)One idea I had a while ago was to use the new synchronizable concept added in #2985297: Generalize the concept of entity synchronization, and make this a "syncing" operation. This way, each hook implementation can decide if needs to act or not based on the
isSyncing()
flag.$error_revision_id
is an empty string.\Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::getEntitySchema()
, which doesn't receive the field storage definitions as a separate argument. The problem with that method is that it's one of the two main entry points that are actively encouraged to be overridden by extending classes, so chaning it's signature would break a lot of code.On the other hand, all the storage (and schema) objects involved in this update process are customly created and have a short life span (they exist only during the update process), so I don't think we should be worried about their internal state.
SqlContentEntityStorageSchema
.revision_translation_affected
. It can be done, but in a followup because it needs quite some discussion and work to be done properly.get...
Comment #30
plach@amateescu, #29:
1: Definitely the name is not great, unfortunately the right one is already taken (
::onEntityTypeUpdate()
). I was hoping that we could leverage PHP docs to clarify that the method handles safely also no-data scenarios. Maybe we should just deprecate::onEntityTypeUpdate()
and make that even clearer through the deprecation note. Happy to pick another name, but I'd really like to avoid mentioning "schema" in the main API entry point also because, once we fix those @todos in theEntityTypeListener
, storage and schema might definitely not be the only affected subsystems. With that in mind, I think you're correct in suggesting that we should just add a new method toEntityTypeListenerInterface
rather than introducing a new interface.3: I read that comment, but I wasn't clear on whether that meant unintended data loss or any kind of data loss. I think there's nothing wrong with intended data-loss. Let me provide an example: one of the main use cases I had in mind for this API, when originally thinking about it, was to switch the entity schema from "mul rev" to "non_mul rev", to remove the performance penalty introduced by the "mul" schema in monolingual sites. If the site only contains monolingual entities, switching to "non_mul" would indeed cause some metadata loss, but no relevant information would be actually lost. If we don't support these scenarios through this API, it's not clear to me how they would be supported otherwise. As for the other missing cases, I'm personally fine with a follow-up as long as it's done in 8.7. Again, I'll ping a release manager to chime in about this.
9: Good point, thanks
15: The reason why I'm suggesting "opt-in" is that I'm scared by the idea of people running schema updates on prod sites without backups. I know this is definitely against good practices, but people are doing this kind of stuff all the time and then are complaining about Drupal sucking as soon as something goes wrong. As part of our effort of making minor updates friendlier, I think it would be preferable for people to have weird tables lingering around rather than not being able to recover their data after a failed update. If we add the hashes suggested in 11 and add a timestamp to the serialized data, we should be able to even run multiple updates without ever risking a collision. A UI (or a cron job) to purge these backups would be a nice to have, not a blocker IMO. I'd feel better if we did this here instead of punting it to a follow-up, but I'm fine with leaving the final word to the committer that will take care of this issue.
16: Right, I forgot about the "static dance" we're doing now :)
19: Very good point: +1 for a follow-up to be addressed once we have more experience with these updates.
21: I swear I was joking :)
7:
Update is probably not always ideal as well, but at least it's assumed to be run multiple times, whereas insert is assumed to be run just once in the whole entity lifecycle.
If I'm not mistaken, that's exactly what the core
forum_node_insert()
hook implementation is doing.I suspect that also the revisionable taxonomy update would break with the current patch, if[wrong: we're saving terms not nodes :)] I guess you're saying it's badly written because it's not using fields/entities, but that was just an example.forum
were enabled.Yep, I guess this something that could happen as well.
This is an interesting idea, the only drawback I can think of is that it shifts the responsibility of dealing with this special use case onto hook implementations. This wouldn't be that bad if no implementation existed yet, but I suspect quite a few would need to be adapted to accommodate this change. The reason why I was suggesting a
::restore()
method is that this is just a storage reshuffle, after all. Ideally we would like no hook to be called at all, which is what would normally happen in an update function if we performed this "migration" manually. Maybe we could just run the "save" operation with a mock module handler?Interdiff review:
We need to update the comment to reflect the logic change.
It should be ok to assume a serial ID, right? This is just an helper after all.
Comment #31
catch::restore($entity)
So is this for only saving to the storage, and not triggering presave/insert/update hooks at all? If we can make that work it sounds the cleanest option to me.
Comment #32
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedI forgot that we have a bug report for this exact situation: #3003935: Entity lifecycle hooks are executed during SqlContentEntityStorageSchemaConverter::copyData. See my comment #2 over there for an additional option that would solve this problem :)
Comment #33
plach@catch, #31:
Well, that was an alternative idea: I think we could manage to perform a
::save()
with no hooks invoked without introducing a new method by instantiating a storage with a mock module handler.Btw, do you think the following issues should be considered blockers for this or is it ok to address them as follow-ups?
Setting back to needs work for the TODOs mentioned in #29 and for the minor update suggested in #30.
Comment #34
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThis fixes the minor comment update from #30 and one of the TODOs, namely #28.13.
The TODO for #28.11 about adding a few characters of a hash of the table mapping to the
tmp_
prefix is not that easy because we are passing in the prefix string when we instantiate the table mapping itself. If we want this short hash to be deterministic, what we could do instead is somehow compute a hash of the entity type + field storage definitions used to instantiate the table mapping. @plach, what do you think?Regarding #28.1 (and #29.1 + #30.1), I just realized that
EntityTypeListenerInterface::onEntityTypeCreate()
is also as broken asonEntityTypeUpdate()
:(Here's why: let's say in 8.7.0 we install a new content entity type in a
hook_update_8701()
function. The entity storage will use the "live" field storage definitions (from code) to install that entity type. Then in 8.8.0, we add a new base field to that entity type with a correspondinghook_update_8801()
function. Now, if someone skips a minor version and updates from 8.6.x to 8.8.0 directly, which is not uncommon at all,hook_update_8701()
will also install the new field added in 8.8.0 andhook_update_8801()
will fail, unless the person who writes that update function is aware of all these shortcomings and makes the code as defensive as possible.So two out of three methods from
EntityTypeListenerInterface
are broken at the moment. I don't really have a good proposal right now, just wanted to start the discussion that maybe we should deprecate that interface entirely.Comment #35
plach@amateescu, #34:
Sounds like a plan. I think it would be good to add also the current request time, just in case for some reason we perform the same update multiple times.
I think this means we need the ability to specify an array of storage definitions on create. The only case where it would be legit to retrieve/specify the live definitions would be on module installation. This would match how we handle the DB schema currently.
Deprecating the interface itself would mean having to find another name. Why is it not ok to just add two new methods and deprecate the old ones? Do you have an alternative terminology in mind that could help with naming?
Comment #36
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedI've been poking at the issue mentioned in #26 in the past few days and I could only make it work with a new
restore()
method, as suggested in #28.7. This also fixes the hook invocation problem :)Since the problem that we need to solve is that
EntityTypeListenerInterface::onEntityTypeCreate()
andonEntityTypeUpdate()
need some additional parameters for field storage definitions, my suggestion is to put "fieldable" somewhere, either in the name of the new interface (FieldableEntityTypeListenerInterface
) or in the name of two new methods on the existing interface:onFieldableEntityTypeCreate()
andonFieldableEntityTypeUpdate()
.Comment #37
plachI don't think we can skip pre-save field methods, e.g.
PasswordItem
would brake.Would it be possible to try and reuse the full save workflow with a mock module handler?
Good call, I'd go with the latter, as we might get conflicting method names between when implementing both interfaces otherwise.
Comment #38
plachOn a second thought PasswordItem is probably fine, since we need to hash the password only when it changes. Can we apply the same line of thinking to the other field types? I.e. if stored values don't change, we don't need preSave field methods?
Comment #39
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThat's exactly how I thought about it, and even documented in the new
restore()
method: that the entity being "restored" needs to be valid/complete from a storage perspective :)Comment #40
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedMissed a spot.
Comment #42
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedIt turned out in followup issues that are using this conversion on real-world entity types that we actually need to invoke at least the pre-save methods.
The reason I went with a streamlined save process in the new
restore()
method is that some parts ofSqlContentEntityStorage
(mostlysaveRevision()
IIRC) can not cope with saving an entity that is neither new nor updated. Using a mock/no-op module handler couldn't have helped us there.Comment #44
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThis was an attempt to fix the behavior of
ChangedItem::preSave()
which was failing in an actual conversion.Updated the patch with a better fix and also added it to the
entity_test_update
entity type so we have explicit test coverage for it.Comment #45
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedForgot to remove the problematic part :/
Comment #47
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThe test methods that update an entity type definition and its field storage definitions to rev/mul are useful outside the new test class added here so I moved them to
EntityDefinitionTestTrait
.Comment #48
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedJust a few improvements found while working on a followup issue.
Comment #49
plachThis is looking very good, however I think we still have a few "open" issues:
Quick code review of the latest changes:
These should be moved into the transaction scope to match the save behavior.
To reduce the amount of duplicated code, would it make sense to move this to a
::sqlStore()
method (better name welcome :) taking a callable as parameter, similar to::wrapSchemaException()
?I'd move this into the base table's if branch...
... and this into the revision table's if branch.
Comment #50
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedWe would need some extra code to retrieve only the default revisions and default translations to make that work. I think it's doable but not in this issue, mostly because I disagree that we should support that at all.
This would be the first time we'd do something like this in Drupal, so I think it needs more discussion. It's a *very* simple change to make and I really don't think it should block this patch.
Well.. this issue is about adding a proper update API, so we can introduce the new
onFieldableEntityTypeCreate()
method in a followup.Fixed points 1, 3, and 4. For 2) it's not that much code so I don't think we need to over-complicate it :)
Comment #51
plachI discussed #50 with @amateescu today in Slack:
Here is my (hopefully) last code review. No blockers besides the missing follow-up references, but some final touches would be welcome :)
We are treating
$sandbox['progress']
inconsistently: sometimes it's an int and sometimes it's a float. Given that we are using the strict equality operator, this is only working because of that ternary operator in the last quoted line. That isn't actually necessary if we always treat progress as a float. This feels fragile to me, at the moment, and it's a critical bit.I'm not sure about those ifs: I think that if temp tables already exist something is wrong, so maybe it would be better to get a schema exception while attempting to create the table. In general all these edge cases around schema creation and table renames don't seem to have test coverage, it would be great to create a follow-up to add that.
The comment doesn't wrap at 80 chars.
We are missing the follow-up links here, setting to NW for these.
typo :)
This is not actually testing multiple steps, even if we create multiple entities exactly to test that case.
Comment #52
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThanks for reviewing, again! :)
Re #51:
$sandbox['#finished']
? In any case, there's no need for strict equality checking there, we don't do that anywhere else in core, so I switched to regular checks instead.SqlFieldableEntityTypeListenerTrait::handleEntityTypeSchemaUpdateExceptionOnDataCopy
is responsible for cleaning up those temporary tables.As for test coverage of all these safety mechanism, that's provided by
\Drupal\Tests\system\Functional\Entity\Update\SqlContentEntityStorageSchemaConverterTranslatableTest::testMakeRevisionableErrorHandling()
, which will be changed to test the new code introduced here in #3004642: Deprecate SqlContentEntityStorageSchemaConverter in favor of the new API provided by EntityTypeListenerInterface::onFieldableEntityTypeUpdate().Also, this blocks #2976035: Entity type CRUD operations must use the last installed entity type and field storage definitions , which is critical.
Comment #53
plachGreat job, thanks for the patience! :)
As mentioned in #30.15, I'd feel better if by default we left the backup tables around. We have a follow-up to address this, but I'd prefer to have a minimal implementation from the very beginning, so that, if for any reason the follow-up is not ready in time, we err on the safe side. I originally suggested a PHP settings switch allowing to enable backup deletion on success, but I'd be fine even with just removing these lines and improving things in the follow-up. I'm literally terrified by the idea of an update being run on a live site with no backup and triggering a silent data loss issue like #2997960: Missing taxonomy hierarchy items in 8.6.0 after running taxonomy_update_8502. In this case exceptions wouldn't be triggered and thus backup tables would be deleted.
As discussed with @amateescu, we will leave the final word to committers.
Comment #54
xjmComment #55
plachPostgres is failing.
Comment #56
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThe pgsql problem is being fixed in #3026290: PostgreSQL constraints are still not renamed properly on table renames. Here's a combined patch to prove that the fix is effective :)
Comment #57
plachThe blocker is in, back to RTBC.
Comment #58
plachRe-uploading #52.
Comment #60
plachFixed deprecation errors.
Comment #61
plachThe interdiff above was trivial, back to RTBC.
@committers:
Please, see #53.
Comment #62
plachComment #63
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThanks, @plach :) +1 for keeping it RTBC.
Comment #64
jidrone CreditAttribution: jidrone at Mobomo for Vibrent Health commentedHi everyone,
I'm working on this patch #2829966: Support for Revisions on groups, and the only thing missing is the db upgrade path.
I don't know if I'm doing something wrong but when I try to convert the group entity to revisionable, I'm getting the following error:
And when I add the status field definition, this is the error:
Exception thrown while performing a schema update. MySQL needs the 'status' field specification in order to normalize the 'group__status_type' index
Please, can some of you to provide an example of how to use this new API method.
Thanks.
Comment #65
amateescu CreditAttribution: amateescu for Pfizer, Inc. commented@jidrone, I posted a comment in that issue :)
As a general guidance for adding a publishing status field to an entity type, you can use the approach that we took in core for taxonomy terms as an example: #2981887-49: Add a publishing status to taxonomy terms
As for converting an entity type to revisionable, you can look at the not-yet-committed patch from #2880149-204: Convert taxonomy terms to be revisionable (the "for review" one)
Comment #66
catchOK I found it hard to find things to complain about, but managed to find two nits:
Could all the checks happen inside the $sandbox['progress'] check? I can't see a reason we'd need to check them every iteration. It's quite a lot of boilerplate to read when first looking at the function.
Is it worth an extra line to create a new entity against the new storage in this test? i.e. we check the schema, we check the entities migrated, but we don't check that saving a new entity to the new schema is happy yet afaict.
Comment #67
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedYay for nits only!
Re #66:
1. Good point, we definitely don't need these checks on every iteration.
2. Sure it is, added :)
@plach pointed out that we also need a CR for this issue, so I'll start writing one.
Comment #68
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedHere it is: https://www.drupal.org/node/3029997 :)
Comment #69
andypostMakes sense to link #2880154: Convert comments to be revisionable
Comment #70
amateescu CreditAttribution: amateescu for Pfizer, Inc. commented@andypost, I've only linked the two issues that I know for sure they have good patches that use this new API. I'll expand the list with other issues as I work through them.
Comment #71
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedAdded a release note snippet as well.
Comment #73
catchCommitted 3148601 and pushed to 8.7.x. Thanks!
Comment #74
plachAwesome, thanks!
Comment #75
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedHere's the followup to make
SqlFieldableEntityTypeListenerTrait
available for all storage handlers, not just SQL ones: #3030483: [PP-1] Make SqlFieldableEntityTypeListenerTrait storage-agnosticComment #76
joelpittetDevel webprofiler busts with this kind of change adding a method to the interface that's not implemented. I'm wondering what that means in contrib for these changes generically, should the contrib maintainer create a 8.7 compatible version?
Comment #77
BerdirThat falls within the BC rule of 1-1 class/interface pairs:
https://www.drupal.org/core/d8-bc-policy
Any real entity storage handler will be fine because they extend from EntityStorageBase.
special cases like webprofiler have to update, but adding a new method will not break 8.6 in any way, so just add the method and you're done.
Comment #78
joelpittet@Berdir, this case it's a decorator, I guess that's the trouble with Decorators. Thanks for the response!
Comment #79
joelpittetI've added the patch to devel here: #3030614: Drupal\Core\Entity\EntityStorageInterface::restore missing on ConfigEntityStorageDecorator
Comment #80
larowlanWould be good to expand the change record to give an example of how to use this to apply changes to field-type schemas
Comment #82
plach