Problem/Motivation
The timestamp field item storage type is [signed] int, which is currently 4 bytes in most DB systems (MySQL and PostgreSQL, SQLite is not affected).
This results in having values limited to the range -2,147,483,648 to 2,147,483,647, which converted to Unix timestamp set the minimum date value 1901-12-13 and maximum 2038-01-19.
See #2795489: 2038 bug with PHP timestamps on 32-bit systems - warn users? and particularly comment #42 . Note that patch was committed so users have been warned about this for several versions now -- this issue is about actually solving the problem.
Proposed resolution
In order to allow these field type to use dates out of range the best solution is to use 'bigint' as storage type, increasing the value memory space from 4 to 8 bytes. The new range will be 9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 quite enough to remove any concern.
Adds a node and node_revision table for the field created in existing fixture, drupal.timestamp-formatter-settings-2921810.php.
Let the existing 'mismatched field definitions' status report for informing that timestamp fields need to be updated. See #136-137.
Remaining tasks
Update Timestamp item schemaAdd test coverage#19batch process the hook_update()write upgrade path tests33.1 set minimum value for #date_year_range(see #57)47 are more test cases needed?48.1 $entity->validate() tests</li>48.5, needs review. Is the test for the timestamp type robust?See #63, #66 and #77. We need a way to update schema on those fields with data, but without removing the checks fromSqlContentEntityStorageSchemaprovide an opt out mechanism #3406172: Provide a flag to allow updates to stored schema for fields- Bail after an amount of time with an error telling people to use the opt-out mechanism and options for doing the alters themselves
User interface changes
No UI changes should be required.
API changes
No API changes should be required.
Data model changes
TimestampItem type int size should be big.
Release notes snippet
A database update will convert timestamp database columns that are created by the entity field system from the 4 byte integer type to the 8 byte "BIGINT" type. This change allows dates greater than the year 2038. This update may take a long time to run on sites with lots of content, but settings.php flags have been added to allow for customizing the update implementation.
| Comment | File | Size | Author |
|---|---|---|---|
| #180 | 2885413-nr-bot.txt | 86 bytes | needs-review-queue-bot |
| #179 | 2885413-179.patch | 11.79 KB | oleh.tarasiuk |
| #136 | Screenshot 2024-05-31.png | 91.83 KB | quietone |
Issue fork drupal-2885413
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
Comment #2
gambryComment #3
juampynr commentedI am facing this issue in a Drupal 7 to 8 migration where there is a timestamp in a custom table with a value of 4294967280. I will skip the issue by changing the value to 2147483647 at hook_migrate_prepare_row().
Here is the error:
Comment #4
gambryIs the original value wrong, or is it really
2106-02-07 06:28:00 UTC?Comment #5
juampynr commentedIt is really 4294967280.
Comment #6
longwaveUnsure why this is 9.x, we could attempt to do this now as this is a bug fix and shouldn't require a BC break?
Comment #8
mikelutzJust out of sheer morbid curiosity...
Comment #9
mikelutzErr, I mean..
Comment #10
andypostKind of conversion pointed by @larowlan in slack https://git.drupalcode.org/project/date_recur/blob/8.x-2.x/date_recur.in...
Comment #11
andypostComment #12
gambryHere are basic tests for the field type, testing its schema support bigint. Setting NR to trigger the tests, this issue is still NW.
Comment #15
gambryThe 124 errors are all related to fields using timestamp type (or types extending it) and requiring definition update.
I'm working on the upgrade path now, so the errors should hopefully disappear.
Comment #16
gambryThis is an initial work for the upgrade path. Is mostly inspired by suggestion in #11, however my feeling is suggested code looks to me overcomplicated, besides just covers a single field type while attached proposal covers
timestampas well as any field type extending it, either been in core (changed,created) or in contrib/custom.For this reason attached code is a WIP, as I believe there is some space for simplifying the logic. Additionally code has 2
TODOs as it looks like the hook_update processes unexpected fields or tables (node_revision__revision_timestamp???).So setting Needs Review in order to trigger the testbot and running the whole tests suite. Locally manual test works and running
modules/comment/tests/src/Functional/Update/CommentHostnameUpdateTest.phpis green.This issue still needs work.
I'm not uploading a interdiff as I'm not changing any of the existing code, only creating the new hook_update() in system.install.
Comment #17
sarathkmThe patch in #16 applies cleanly but the timestamp field in UI was still set to 2037. Also database update did alter the existing field column having int to big.
In this patch there I have updated the UI to include max year as per bigint limit so as to allow date beyond 2037.
Comment #18
gambry@sarathkm can you provide an interdiff with your changes?
I know it's clear what line you changed, but always worth adding the interdiff. :D
Comment #19
gambryAlso where did you get that value? I can only see
15:30:08 UTC on Sunday, 4 December 292,277,026,596as end of a valid 64bit range?Also if we move the max value, we should also move the min one?
Comment #20
pasquallesome basic questions:
1. Is the database schema update allowed for Drupal 8?
2. Can we have a rough estimate how long the update will run?
3. Is there some (node count) limit where running this update is not recommended?
Comment #21
gambryYes. As schema is considered @internal API, changes to internal API are allowed on minor releases.
On my 2.2 GHz Intel Core i7 laptop (way less powerful than an average server),
ALTER table node_field_data MODIFY `created` bigint not nullagainst 100k rows runs in 3s (avg., first call can take longer maybe due mysql cache rebuild or some connection overhead). Through Drupal I get similar results, counting the overhead of processing the request and loading the response.If anyone has access to a bigger dataset please test and provide your data.
Actually the upgrade path is not wrapped in a batch, and probably it should?
The batch should prevent most of the limitations, however taking the rough numbers from the point above: if your PHP max execution time is 150 seconds (standard 2.5 minutes) I would avoid running this update on tables with more than 5,000,000 rows.
But again if you are dealing with bigdata, is likely for your server to be much more powerful than my laptop and so for you to support higher rows than me.
Worth mentioning this is not the first time a schema change is made against a field, see node_update_8300() for an example of an
intmodified totinyint.Comment #24
etroid commentedRe-rolling patch
Comment #25
hardik rawal commentedThe patch in #17 does not work with 8.8.8, the patch fails to apply. Also the hook_update_N count needs to be updated since the system.install as per 8.8.8 version. The revision tables are not updated.
Comment #26
gambry@Hardik Rawal the issue is currently targeting 9.1.x, which is the latest development branch. Feel free to re-roll the patch for 8.8.x if needed.
The revision tables should be covered. 🤔 But as this doesn't have yet upgrade path test I can't say for sure.
And as we are not gathering the attention for reviewing, let's move back to work to tackle:
Comment #29
pameeela commentedClosed #2911684: Dates cannot set before 1901-12-13 on 32bit as a duplicate so adding ProFire and mpdonadio for credit here.
Comment #32
quietone commentedReroll and added an update test.
#26.
1. Changed according to https://en.wikipedia.org/wiki/Unix_time
3. Added test.
That leaves #2 doing this in a batch process.
Comment #33
alexpottShouldn't 1902 also change here? 292277026596 seems a very big number :)
I think we can do better than this for an interesting date in the past - perhaps something from https://www.quora.com/What-is-the-first-recorded-date-in-human-history
I think we need to batch this update and we also should add some very very good logging to the 'update' channel of exactly what we did and why.
Is this needed?
Need adjusting.
Comment #34
quietone commented1. Needs work. Note the docs for strtotime state, "For 64-bit versions of PHP, the valid range of a timestamp is effectively infinite, as 64 bits can represent approximately 293 billion years in either direction. "
2. Changed to the dates of Halley's comet.
3. That is an existing todo, #21, #26. It is something I don't know how to do.
4. No it is not needed. Removed.
5. Comment changed.
Comment #35
quietone commented33.1 Still to do
33.3 This patch adds batching this update. Still to do here is the logging.
Also, in this patch the comments have been tweaked and a long if statement has been broken in to two if statements for readability.
Comment #37
spokjeRrrrrrrrrrandom Test Failure #3214565: [random test failure] Random fail in BuildTestTest::testPortMany, back to NR to trigger a retest.
Comment #38
renatog commentedPlease could we use empty() instead of simple verification?
From:
To:
Comment #39
quietone commented33.1 Still to do
33.3 Added very basic logging and tests the count of logged 'success' messages. Needs works for "some very very good logging to the 'update' channel of exactly what we did and why". Ideas welcome.
#38.1 forgot to do. @RenatoG, what is gained by using empty() in this case?
Comment #40
alexpottI don't think this log is that helpful. I think we need a log on all possible points where we return from this. Any exceptions that are thrown will get their own logs anyway.
These early returns need logging.
Could this be?
The foreach to loop around a single item array threw me for a bit.
We're testing the values in the schema we've stored but we're not testing that we've called
$schema->changeField($table, $columnName, $columnName, $specification);correctly and updated the field on the database layer.Comment #41
renatog commented@quietone yes, using empty() according to this guide
So maybe isn't strongly necessary, but is a good practice to do. What do you think?
P.s. apart from that, according to the points reported on #40 I'm changing the status for needs work okay?
Comment #42
quietone commented@alexpott, thank you.
#40
1. Yes, I know. I didn't mean to leave that in.
2. Logging added at those returns but I have yet to think about how to test those.
3. Oh dear, that would look odd. I had been playing with the batch size because I only know batch from migrate. And then when I set it to 1, I must have moved on to other bugs and forgot to tidy up. This is changed to your suggestion.
4. Added testing of the database columns.
@RenatoG, I must get to bed, will respond later.
Still to do:
Presumably, testing for 40.2
#41.
Comment #43
renatog commentedGreat! Thank you so much
Added a new patch with a minor grammar fix in a comment
Comment #44
quietone commentedThis patch adds a new timestamp field in order to prove that a 'big' timestamp field is not changed. During testing I was getting an error something like 'field needs to be updated' and then found SqlContentEntityStorageSchema::requiresFieldStorageSchemaChanges which does robust testing to determine if the schema needs to be changed. This update function is now using that methods. I suspect more if blocks can be removed but that can happen as test cases are added. I also did some clean up in _system_update_field() so that does as little as possible so it can return early if there is nothing to do.
The variables in the new functions in system.install have a mixture of lowerCamelCase and lower_case and I want to clean that up next, it is annoying.
Comment #45
quietone commentedThis patch does the renaming mentioned in #44, includes work on the comments and changes so that temporary variables can be removed.
Comment #46
quietone commentedThis gets the correct tables using $table_mapping->getDedicatedDataTableName which simplifies the code.
Comment #47
quietone commentedI am still a bit fuzzy on the test cases needed here. Are more needed?
And what is to be done to help those that have extended TimestampItem? Say they are not using 'value' as the column, or the timestamp is not the mainproperty?
Comment #48
berdirShould we also call $entity->validate() here to make sure that these timestamps pass validation and a negative test with a higher value that is not allowed? not sure we even can do that, FWIW.
well. this is going to be a scary update on big sites :-/.
Also wondering if we can get away with making it a post update. entity updates usually are, and this shouldn't cause any problems when not updated beside entity update definition manager complaining.
the field map already has the field names too, I don't think we need to load the field definitions just to show that
So you really just need the keys of $fields, which you in fact already do with the array_intersect_key() here.
Meaning, just:
wondering if we should just once, in the update function init for example, clear caches instead of keep disabling them. The first time we save a change, they are cleared anyway.
Yes, as expected, scary stuff.
I do wonder though. Could we bake support for changing the size, at least making it bigger than before, into \Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema?
Then we could just do a updateFieldStorageDefinition() and keep the dark magic contained within that class.
That would be \Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::updateDedicatedTableSchema() and \Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::updateSharedTableSchema()
We would need to get \Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::hasColumnChanges() to be OK with an "upsize" of integer fields specifically and then implement the necessary logic for that below.
The advantage is that the call to \Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::saveFieldSchemaData() will take care of the installed schema and last installed definitions stuff.
There are downsides to that, we can not specifically apply just size change, it will at least re-create the indexes, but that might be required anyway?
I also haven't actually tried any of that.
should we maybe use the filled database dump here to have those updates tested with data in those tables?
> And what is to be done to help those that have extended TimestampItem? Say they are not using 'value' as the column, or the timestamp is not the mainproperty?
Well, I do hope nobody did that, but from what I see, the code does check whether the value property is there. Technically, we could check on the field definition if the value property is defined, but honestly, I think that's a very far-fetched edge case. Why would you extend a timestamp field if it doesn't have a timestamp? Nothing in TimestampItem is going to be of any value to you then.
Comment #49
quietone commented@Berdir, thanks for taking the time to review.
#48.
1. Added validation via entityValidateAndSave. Added checks for the min and max values and the min value fails. This needs more work.
2. Moved to a post_update hook. No surprises doing that but then I have never done one before.
3. I suspected that was over complicated but it appears I never got back to investigating that. Thanks!
4. Adding clearing the cache in _system_update_get_timestamp_fields(). Is that what you meant?
5. Skipped the scary bit this time.
6. Changed to using the filled database fixture. That changed the list of fields, $timestampFieldStorage, in the test.
Todo.
48.1 - the test will fail
48.5
Comment #51
quietone commentedAnother iteration.
48.1. The test fails when setting the timestamp to the minimum value. The value becomes a float instead of an int and that results in "This value should be of the correct primitive type.".
48.5. I am not convinced this is robust, it just checks the data type to allow changes for timestamp, in \Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::updateSharedTableSchema. It does seem a much better approach though. If this is the way to go a similar change in needed in updateDedicatedTableSchema (as mentioned in #48) and that will need tests. Haven't even thought about that yet.
Comment #53
mstrelan commentedSeems the following remaining tasks in the issue summary can be marked off:
And I've added the following:
Comment #54
quietone commented@mstrelan, thanks for updating the IS. It makes working on this easier.
This patch updates the min value for TimestampItem and adds a test of values out of range.
Validation of the entity was added in #49 by using $
this->entityValidateAndSave($entity);. Updating the IS.33.1. I am wondering if changing the date range on the widget should be in a followup because the widget only handle 4 digit years. It seems prudent to discuss in a separate issue how to handle 12 digit years.
edit: fix typo. s/I/It/
Comment #55
berdirnot a huge fan of dataprovider tests in kernel tests. each one needs to go trough the setup again. Not a huge deal on kernel tests compared to browser tests, but still, quite a bit of overhead.
This also proves what I suspected, that it very weird to test the validation outside of those ranges, because they are not even integers anymore. strange that the negative value still works though. I think the reason for that is that we're actually using the wrong number here, the lowest int64 number is one lower: https://stackoverflow.com/questions/6003492/how-big-can-a-64bit-signed-i.... So, not that it matters, but strictly speaking, our validation of it is wrong ;)
I'd be fine to just drop that test then, sorry for the extra loops
Comment #56
quietone commentedNo worries, removing test.
Yes, the minimum value behaves strangely. Using PHP_INT_MIN returns an int but using the value of PHP_INT_MIN returns a double.
Comment #57
quietone commentedOops,
Also, decided to update the date range for the widget, which I got from this.
[-9223372036854775807] -29227700493-05-20 06:56:53
[9223372036854775807] 29227704433-08-14 17:03:07
Comment #58
gambryI wish Drupal could still be a thing in the summer of 29227704433!!!
...although in that case this code must be fixed again before then. :D
Comment #59
berdirI'm more concerned about having to find a new planet to use Drupal on (or a new universe?) than switching over to int128, that should be widely available by then :)
Comment #60
gambryQuick review of the remaining tasks. From what I can see 33.1 is done on #57.
Comment #61
quietone commentedThe parent issue was set to Critical before this was split to child issues. Marking this Critical as well.
Comment #62
ghost of drupal pastComment #63
ghost of drupal pastThanks for the patch.
First, you need to check whether the entity is stored by the standard SQL schema see https://www.drupal.org/files/issues/2021-07-05/838992-54.patch
user_update_9301first three lines.Second, I really don't like how
if (!in_array($type, ['timestamp', 'changed', 'created'])) {stays forever. Sure, it enables this update but it removes a protection for these types forever. I have a very hard time wrapping my head around the potential consequences. I filed #3222270: Allow bypassing the update validation in SqlContentEntityStorageSchema with my suggestion. I really am grateful for this issue to bring this problem up because it solves a rather big issue my module has.Third, I love the Halley's Comet tests :) however I would love it even more if we were to reload the entity and checked the values are there.
Comment #64
quietone commented@Charlie ChX Negyesi, thanks for the review! Thank you for the compliment on Halley's Comet. I don't follow what you mean by removing a protection from timestamp', 'changed', 'created'.
This patch makes changes for the first and third point in #63. It was all rather straightforward, except resetCache is now called inside the loop in _system_update_get_timestamp_fields.
Comment #65
daffie commentedComment #66
ghost of drupal past> I don't follow what you mean by removing a protection from timestamp', 'changed', 'created'.
After this patch, the "The SQL storage cannot change the schema for an existing field" exception will no longer be thrown for these field types. That's what I meant.
Comment #67
ankithashettyRe-rolled patch in #64 as requested in #65, thanks!
Comment #68
daffie commentedThis does not work for PostgreSQL and SQLite. This will get fixed in #301038: Add a cross-compatible database schema introspection API. Therefor postponing this issue.
Comment #69
berdirThat's a very old and I assume complex issue to postpone this critical issue on on. This is just a test, I think we can possibly find a way to verify this without requiring to query the information, we could for example just test implicitly by trying to write a value there or so. Will have a closer look also at the update field related changes.
Comment #71
eugene.britApplied this patch to the 9.2.x version
Also, added try/catch to avoid exceptions for timestamp fields with data
Comment #72
eugene.britFixed code styles, please ignore #71 and use this one.
Comment #73
eugene.britFixed exception in update of field storage definition for fields with existing data
Comment #74
catchRan this on decent sized database (largest entity table is half a million rows):
Not bad at all. Probably worse if you get into tens of millions of entities, but those sites should be running updates on a local/staging environment before they get to production.
Comment #75
ravi.shankar commentedAdded reroll of patch #73 on Drupal 9.4.x.
Comment #76
andregp commentedChanging status for the last patches added.
Comment #77
ghost of drupal pastIt seems the concerns I raised in #63 are still valid -- although it's possible of course I was communicating them badly: currently, there is an exception thrown when an attempt is made to change the SQL schema of a field. By adding
if (!in_array($type, ['timestamp', 'changed', 'created'])) {if a future update attempts to change the SQL schema of a field of these types then the exception will not be thrown. I suggested a generic solution in a separate issue.Comment #78
gambry@Charlie ChX Negyesi raised a good point in #77.
Updating the issue summary to reflect this. I'm not sure if there is any workaround we can do on this issue, or if we need to wait for something like #3222270: Allow bypassing the update validation in SqlContentEntityStorageSchema or #301038: Add a cross-compatible database schema introspection API to land first?
Comment #80
daffie commentedNeeds a reroll for 10.1
Comment #81
mrinalini9 commentedRerolled patch #75 for 10.1.x, please review it.
Comment #82
mrinalini9 commentedFixing custom commands failure issues in #81, please review it.
Comment #83
daffie commentedThe upgrade path test is still needed.
Comment #84
quietone commentedNeeds a reroll
Comment #85
quietone commentedForgot to run the code checks.
Comment #86
quietone commentedA first attempt at a test.
Comment #87
catchNit: 9.4.0
Nit: big?
Comment #89
quietone commentedThis patch makes the addresses #87, and fixes another comment as well.
Comment #91
quietone commentedWell, the test passes locally so a bit stuck on what to do next.
+++ b/core/modules/system/system.post_update.php
@@ -51,3 +56,132 @@ function system_post_update_linkset_settings() {
+ try {
+ \Drupal::service('entity.definition_update_manager')->updateFieldStorageDefinition($storage_definition);
+ }
+ catch (FieldStorageDefinitionUpdateForbiddenException $e) {
+ }
+
+ $logger->notice("Successfully updated entity '$entity_type_id' field '$field_name' to remove year 2038 limitation.");
If the exception is thrown, it just falls through to log the success message. That needs to be fixed. It should log a useful message when the exception is thrown. This failure is not tested and I am not sure how to test it. Anyone?
For background, the try/catch was added in #71 to "avoid exceptions".
Comment #92
catchWhat happens if we just remove the try/catch?
Comment #93
quietone commentedLet's find out.
Comment #95
catchOK so it looks like it's not needed, in which case I'd leave it out - if we're not able to update the entity definition something is wrong. Not nice when updates throw exceptions but not nice when they silently fail and leave your site in an inconsistent state either. The other option would be to catch the exception but log it as and error I guess.
Comment #96
gambryTrying to resurrect this at Drupal Global Contribution Weekend.
Applying and running the test locally, to see what can be done.
Comment #97
gambryRe-testing #93. Also adding other DB types to validate this is not a DB type and we haven't tested this patch against other DB types for almost 1 year, so to create a newer baseline.
UPDATE: running scripts/run-tests.sh locally fails in the same way, so hopefully, we can nail the problem.
Comment #98
eugene.britRe-roll #89 for 10.0.x
Comment #100
brandonpost commented#98 worked for me on 10.1.1, PHP 8.2, and MySQL 8. Thank you!
Comment #103
brandonpost commentedActually, here's a re-roll for 10.1.1. #98 didn't apply completely clean on 10.1.1 because of a couple offsets.
Comment #104
brandonpost commentedHere's a re-roll for 11.x
Comment #105
catchTest failures are real - are we missing a file adding that test field or similar? #93 has different test failures.
Comment #106
brandonpost commentedSorry @catch, I didn't do my due diligence before posting the last 2 patches. I just grabbed the last patch posted and rerolled it for 10.1.x and 11.x. I grabbed patch #98, which was a reroll of #89, which means it missed the work you and @quietone did in #91 - #95.
Here is a reroll of #93 for 11.x
Comment #107
brandonpost commentedLooking at the remaining tasks in the issue summary, it looks like one of the main things left to do is address the issue mentioned in #63, #66, and #77. The way this was worked around in #93 was to bypass the result of hasColumnChanges() using this code:
The problem here is that 'timestamp', 'changed', and 'created' fields will forevermore be able to bypass hasColumnChanges().
hasColumnChanges() was added in issues #2544954 and #2542748 because SqlContentEntityStorageSchema cannot handle column changes. But in this case, the column changes in the database have already been handled in _system_update_process_timestamp_field() before getting to SqlContentEntityStorageSchema.
So what if we just set a flag on $storage_definition in _system_update_process_timestamp_field() to let SqlContentEntityStorageSchema know that the column changes have already been handled, like this:
Then check for the flag in SqlContentEntityStorageSchema before hasColumnChanges() like this:
It seems like this issue of synchronizing changes in database schema with the field storage definition stored in config would also affect how custom modules update the schema for their custom entities. @catch mentions this in the last paragraph of 2542748-24. How is it that custom modules work around this hasColumnChanges() issue to change the columns of a field on a custom entity? Couldn't the same thing be done here to update these timestamp fields? (Sorry, I'm a longtime D7 developer, so I'm still getting up to speed on all things D10/11, like how things like custom entity updates work.)
About the failed tests, I took a look at them, but don't see why they would fail. The one test says it fails to find the success message in dblog. But that must mean the other tests that check for the new field specifications passed. It seems hard to believe that the code would succeed in setting the new field specs, but fail in setting a simple log message. I would be interested to see the html it fetched from $this->drupalGet("admin/reports/dblog/event/$event") to see if it actually got a dblog page or maybe a 404 page or something else.
The other test that fails just says 'Unknown', so I don't know what to make of that or what test it is even referring to.
Comment #108
catchThat seems like a good idea, let's try it :)
Comment #109
brandonpost commentedGreat! That idea is implemented in patch #107 if you'd like to review it, although the tests failed on the patch because of those other separate issues with the tests (as described at the end of #107).
I'm not sure if it violates any standards/policies to use the setSetting()/getSetting() functionality on BaseFieldDefinition in this way. If so, we could look for another method of setting the flag.
Comment #110
mandclu commentedI have run into a similar issue in terms of trying to get the stored schema definitions updated on a field with existing data, in my case for the Smart Date contrib module, which uses timestamps for date and time storage. IMHO it seems a little crazy that as a contrib author I can easily update the database tables to increase the storage size, but core's schema definition manager refuses to update the stored schema definition if there is existing data, even if the tables themselves have already been updated. This leaves me with a warning about the definitions needing to be updated that can't be fixed, at least using normal schema update methods.
I tend to think that the schema update restrictions shouldn't apply to updates that increase the storage, though I suppose that in practice this might be nontrivial to implement. I like that the patch in #107 introduces a flag that can be used for cases where the database have already been updated, so updating the stored definitions is needed to bring them up to date.
I'll take a look at the failures to see if I can get the tests to pass, since having this flag available to get schema definitions updated would be invaluable.
Comment #111
quietone commentedthis is just an update of the patch.
Comment #113
quietone commentedFix Y2038SchemaUpdateTest.php by searching the logs for the 'wid' of the first log message we want to check.
Comment #114
quietone commentedRemove unused use statement and tidy some use statements.
Comment #115
quietone commentedY2038SchemaUpdateTest.php passes locally but is still failing on drupalci.
The other failure is because the new updates are looking to change the storage tables for all the instances of a timestamp it finds. The existing test TimestampFormatterSettingsUpdateTest.php adds a timestamp field but it does not create the association node and node_revision tables. Then, when the new update run it will fail because the tables do not exist. This change adds a node and node_revision table for that field in the fixture, drupal.timestamp-formatter-settings-2921810.php. That allows the test to pass locally.
Comment #116
smustgrave commentedSeems to have a test failure.
Comment #117
quietone commentedThe fix was to rework the test to not rely on the order of the log messages. And instead of loading the the log page, admin/reports/dblog/event/N, it now just checks the value stored in the database table.
Comment #118
quietone commentedI converted to an MR and am hiding the patches. Removed the tag, "Needs upgrade path tests", because a test exists.
I think this item, "See #63, #66 and #77. We need a way to update schema on those fields with data, but without removing the checks from SqlContentEntityStorageSchema" from the remaining task is resolved by using the new flag. See #107 and #108
There is still #48.5
Comment #119
quietone commentedI was wrong #51 made the updates for #48.5. I will strike this and the item mentioned above out from the remaining tasks.
So, I think we are left to reviews and making sure testing is sufficient.
Comment #120
quietone commentedAdd a change record
Comment #121
smustgrave commentedApplied the MR.
Ran updb and got
So that ran without issue.
Rebased so I can run the test-only feature and got
Think this is good.
Comment #122
catchI think we should add an additional change record for the 'column_changes_handled' setting - since that could potentially be used by contrib, including to fix 2038 bugs if there's a non-timestamp field type storing a timestamp out there.
Also some minor comments on the MR.
Comment #123
quietone commentedComment #124
smustgrave commentedMR is still green and new CR reads well I think. Remarking.
Comment #125
alexpottI've added some questions to the MR. I think we need to deal with an empty
$sandbox['items']Comment #126
alexpott@Berdir I see you recommended a post update. I'm not sure. @catch and I debated this. See transcript below.
The tldr; we settled on moving this back to a hook_update_N() function.
Comment #127
berdirI'm ok with keeping it as a regular update. I think beside the usual things reasons for post update or not, my thought was that technically a side could have a workflow that would allow it to respond to requests again while it's running post updates. But I'm not even sure that's possible.
I didn't review the MR since a long time, but I'm still concerned that the update, post or not, is going to to be a pain on big sites with hundreds of thousands of nodes and other entities. It's a lot of fields to update and each is going to take minutes. What are we going to do if that causes an hour downtime or something?
What I'd propose is as settings flag to skip this update. Either custom for this, or a more generic system, like $settings['skip_update'] = ['system_post_update_y2038', 'system_update_12000').
We could be nice and provide this as a custom console command (not sure if we'd need to add batch support for that), or there could be a module that provides a drush command. Then you can update, and then run this in the background. it's going to slow down your site and you might will get locks/long waits on table writes, but at least you're not in a maintenance mode for an hour or more.
Comment #128
alexpott@Berdir I agree - having a way to opt out of the update and manage it on yourself seems sensible. I think we need to provide this mechanism. I also think the update should bail after an amount of time and error telling people to use this setting and options for doing the alters themselves. I know that Percona has a tool for altering tables for example. I think that this means we also need to provide something in the status report to tell you that your timestamp fields have been fixed.
Comment #129
mandclu commentedAs a contrib maintainer, I've been following this issue closely, because the ability to allow stored definitions to be updated after database columns have already been upsized would be invaluable for me. It sounds like there is still some distance to cover before the proposed changes here can be merged. Would it be possible to introduce that specific change, to introduce the optional "column_changes_handled" setting, in a separate issue more quickly?
Or, looking at this another way, is there a precedent for the "opt out" update path? If so I'd be happy to help adapt an existing example to what's needed here.
Comment #130
mandclu commentedThe possibility of an empty
$sandbox['items']was cited as the key reason to move this to Needs Work, so moving this back to Needs Review, since the code has been updated to handle this possibility.I have also created #3406172: Provide a flag to allow updates to stored schema for fields as a smaller change to only introduce the column_changes_handled setting.
Comment #131
catchThis still needs to move back to a hook_update_N() with a $settings opt-out per #126-128.
Comment #132
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #133
quietone commentedAdd items form #128 to the remaining tasks.
Comment #134
mxr576Reassigned the change record to the dedicated issue that introduces the special flag only in #3406172: Provide a flag to allow updates to stored schema for fields.
Comment #135
mxr576Comment #136
quietone commentedAdded a setting to allow skipping the update. The skip is done in the update function. Should it be in update_do_one instead?
I am not sure how to implement a timeout as asked for in #126. How to do that?
There is an item to add an item to the status report indicating if the timestamp fields have been fixed or not. Before adding that I'd like to know if the existing Error from the Entity system sufficient for this or not. Attached is a screenshot of those errors from a site where the update has not been run.
Comment #137
catchIn the update function is fine I think, keeps the logic together.
The 'mismatched field definitions' status report items seems plenty to me - sites will only get that if they explicitly skipped the update, so they ought to be able to put two and two together.
Comment #138
quietone commentedmade an attempt at adding a timeout.
Comment #139
smustgrave commentedUpdated CR slightly to include additions to the default.settings.php
Looking at the open threads appears @quietone has addressed them all.
Applying MR locally update hook runs fine.
Believe this one is finally ready
Comment #140
alexpottI've added some review comments to address. I think we can make the timeout handling a bit simpler. And we can inform the user better when things are skipped.
Comment #141
quietone commentedI have responded to all the feedback from alexpott. Time for another set of eyes.
Comment #142
smustgrave commentedFeedback appears to be addressed
Only question I have is the update hook does it need to be 10301 if going into 10.3? Does it matter?
Comment #143
catchThis won't go back to 10.3, it might only get committed to 11.1 at this point.
Comment #144
smustgrave commentedSo just update hook needs to be updated?
Comment #145
catchI didn't mean to set this to needs work, that was a mistake, but actually yes.
Also this is going to be soft-postponed on #3108658: Handling update path divergence between 11.x and 10.x - we can't commit it until that issue lands and might have to use the helper method from that issue in this one, in case we ever did backport it to 10.4 for some reason.
Comment #146
smustgrave commentedSounds good.
Comment #147
quietone commentedAdding issue this is postponed on to the issue summary per, https://www.drupal.org/docs/develop/issues/fields-and-other-parts-of-an-...
Comment #148
catchThe update path divergence issue just landed so this is unpostponed, but it will need to implement the new API (even if we only commit it to 11.1.x) so moving to needs work.
Comment #149
quietone commentedI am assuming this is for the next minor only, 11.1.0. Therefore changed to system_update_11001 with no equivalent update.
Time for reviews again.
Comment #150
smustgrave commentedSince this was previously RTBC before the blocker, and the one update hook version updated. All threads were previously reviewed. So going to re-mark it.
Comment #151
catchI re-reviewed and have one question on the MR still, looks RTBC to me apart from that though.
Comment #152
smustgrave commentedTesting out if we can remove that line, if it breaks I'll revert back
Also IDE picked up a variable that was never used so removed that too.
Comment #153
smustgrave commentedSo I tried by removing the cache clear and it caused a few test failures https://git.drupalcode.org/issue/drupal-2885413/-/pipelines/262767
Added back and back to green so appears it's needed.
Comment #154
catchThose test failures look unrelated to me? https://git.drupalcode.org/issue/drupal-2885413/-/jobs/2534394 is FileDownloadTest which doesn't involve the update path, and the other one I saw was Nightwatch. Which relevant tests are failing?
Comment #155
smustgrave commentedSwear thought I re-ran it but guess I was wrong.
Comment #157
catchThanks, looks like we can just do without that - cache will be cleared after running updates anyway.
Committed/pushed to 11.x, thanks!
We still need to decide whether we're going to backport this to 10.4.x.
The advantage of backporting is that sites would be able to get this update over and done with before they need to update from 10.4 to 11.2 or something like that. The disadvantage is that if they have a massive site, it could be unexpected to get such a long running update in a minor release.
Added a release note to https://docs.google.com/document/d/1Df_zLpRIoM44pq4vVGxjPPA6G0m0wgocIAly...
Published the CR.
Comment #159
jurgenhaasThe change records states that the default value for
timestamp_field_update_y2038is TRUE, which would make sense for most sites. But insystem_update_11001it uses the default FALSE, which means that the update will be skipped.Comment #160
catch@jurgenhaas I think it's OK, but it relies on a double negative:
We could probably change that to
I think the reason it uses the double negative is because we never expect anyone to actually set
$settings[''timestamp_field_update_y2038'] = TRUEbut only to FALSE if they do something custom.Comment #161
jurgenhaas@catch I'm not sure, there is a double negative here.
This returns FALSE if
timestamp_field_update_y2038is not set in the settings.php, which is what we have in all Drupal installations by default.Because of that, this if-statement is giving us TRUE:
And therefore, it outputs the message, exits from that update hook and Drupal marks the schema as if the hook was executed. But it wasn't.
I believe, the correct statement should be this:
Comment #163
catch@jurgenhaas yes you're right - pushed a new branch. https://git.drupalcode.org/project/drupal/-/merge_requests/9349
I don't understand why the tests are passing with the wrong logic though.
Comment #165
catchWent ahead and pushed the follow-up since it's a trivial change, but we should improve the test coverage here before a 10.4.x backport.
Comment #166
jurgenhaasThe tests seem to be testing the schema. Since the test is running in a Drupal site which got installed with the latest schema already, there is no need to execute
system_update_11001, I guess. So that is never tested, but all the rest of it is.Comment #168
jurgenhaas@catch the commit from #164 would not fix the issue. It should be this:
Comment #169
catchY2038SchemaUpdateTest explicitly tests the before/after so ought to have picked this up.
Comment #171
catchOof you're right, default needs to be changed for sites that don't have anything set. edit: pushed another follow-up commit.
Comment #172
quietone commentedThe backport is fails on this update when run in test, TimestampFormatterSettingsUpdateTest.php.
Storage is created for field 'foo' in, drupal.timestamp-formatter-settings-2921810.php, but not an instance.
Comment #173
alexpottRe #172This is valid. A storage can be around without instances. There is an option on field storages to persist even if no instances exist. We do this for the body field for example. It is rarely used but it is possible. So the code should cope with this.
Comment #177
catchRe #172 and #173 that sounds like a real bug, we could be more defensive and check the column exists before trying to change it, but it would be better to filter out storages without instances before we get there.
Went ahead and reverted the three 11.x commits here so we can fix that issue in 11.x first, then backport it all together to 10.4.x.
Comment #178
quietone commentedAdded testing of skipping the update as well as testing with a field that does not have a database column. The test first tries to skip the update but that fails with
because \Drupal\Core\Field\Plugin\Field\FieldType\TimestampItem::schema has changed but not the database columns. This is the same type of error given when running the update with a field that does not have database columns.
What are your ideas on how to proceed?
Comment #179
oleh.tarasiuk commentedreroll #98 for 10.3.x
Comment #180
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #181
quietone commentedThe CR for this was still published. Unpublished and added a release note snippet
Comment #187
quietone commentedComment #188
newaytech commentedJust been bitten by the Y2038 bug by a credit card expiry date being beyond the INT range. Using Drupal Commerce - fully up to date - and Drupal core 10.5.6. Will the patch on #179 work until we see this in core?
Comment #189
catch#188 shows that sites are already getting bitten by this even though 2038 is 13 years away. This would be good to get done for 11.4/12.0 so adding the release priority tag.
Comment #190
quietone commentedComment #191
quietone commentedComment #192
marcos_lima commentedWe've also encountered this bug. With the Acquia DAM (acquia_dam) module, we cannot utilize assets that have expiration dates set beyond 2038, which impacts many of them, as our use case often requires assets to expire in 2040.
Comment #193
catch@marcos_lima while this is a core bug, I'd be interested to know if you've filed a support request with Acquia DAM considering it's a paid subscription service. This issue nearly got fixed once and only needs a bit more help to get fixed properly, but the people who most recently pushed it forward are quite overstretched.
Comment #194
mglamanRE #192 193, I think the bug is that acquia_dam is using a timestamp field to track expiry: https://git.drupalcode.org/project/acquia_dam/-/blob/1.1.x/src/Entity/Me...