Problem/Motivation

The time fields defined in hook_schema or pseudo hook_schema (i.e BatchStorage::schemaDefinition()) need to be updated for Y2038. This issue is to fix this in the following:

  • core/modules/forum/forum.install
  • core/lib/Drupal/Core/Batch/BatchStorage.php
  • core/lib/Drupal/Core/Cache/DatabaseBackend.php
  • core/lib/Drupal/Core/Flood/DatabaseBackend.php
  • core/lib/Drupal/Core/Queue/DatabaseQueue.php
  • core/modules/comment/comment.install
  • core/modules/dblog/dblog.install
  • core/modules/history/history.install
  • core/modules/locale/locale.install
  • core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
  • core/modules/migrate/tests/src/Unit/MigrateSqlIdMapEnsureTablesTest.php
  • core/modules/migrate/tests/src/Kernel/HighWaterTest.php
  • core/modules/statistics/statistics.install
  • core/modules/system/system.install
  • core/modules/taxonomy/src/TermStorageSchema.php
  • core/modules/tracker/tracker.install
  • core/modules/views/src/Tests/ViewTestData.php
  • core/modules/views/tests/src/Kernel/Handler/FieldDateTest.php

Steps to reproduce

Proposed resolution

Change the size.

We can skip updating the time fields for SQLite, because SQLite uses a more general dynamic type system. The size of the value determines the size that is used on disk. For more info: https://www.sqlite.org/datatype3.html.

Remaining tasks

Patch
Confirm that all the schema s have been fixed here, that none were missed
Review
Commit

User interface changes

API changes

Data model changes

Release notes snippet

CommentFileSizeAuthor
#76 3215062.73_76.interdiff.txt919 bytesdww
#76 3215062-76.patch25.01 KBdww
#74 3215062.67_73.interdiff.txt4.31 KBdww
#74 3215062-73--10.1.x.patch25.15 KBdww
#72 3215062 after patch d10.png82.89 KBaarti zikre
#72 3215062 after patch db.png25.74 KBaarti zikre
#72 3215062 afterpatch.png79.51 KBaarti zikre
#67 interdiff--50-79--10.0.x.txt1.67 KBgambry
#67 interdiff--50-79--9.4.x.txt1.67 KBgambry
#67 3215062-79--10.0.x.patch25.14 KBgambry
#67 3215062-79--9.4.x.patch25.13 KBgambry
#50 3215062-50--10.0.x.patch24.89 KBravi.shankar
#50 interdiff_48-50--10.0.x.txt1.4 KBravi.shankar
#50 interdiff_45-50--9.4.x.txt1.4 KBravi.shankar
#50 3215062-50--9.4.x.patch24.88 KBravi.shankar
#48 interdiff_43-48.txt2.11 KBravi.shankar
#48 3215062-48.patch25.15 KBravi.shankar
#45 interdiff-3215062-43-45.txt2.11 KByogeshmpawar
#45 3215062-45.patch25.14 KByogeshmpawar
#43 interdiff_41-43--10.0.x.txt1.63 KBravi.shankar
#43 3215062-43--10.0.x.patch25.07 KBravi.shankar
#43 interdiff_41-43--9.4.x.txt1.63 KBravi.shankar
#43 3215062-43--9.4.x.patch25.06 KBravi.shankar
#41 3215062-41--10.0.x.patch25 KBgambry
#41 interdiff--39-41--10.0.x.txt1.51 KBgambry
#41 3215062-41--9.4.x.patch24.98 KBgambry
#41 interdiff--39-41--9.4.x.txt1.51 KBgambry
#40 3215062-39--10.0.x.patch25 KBgambry
#39 3215062-39--9.4.x.patch24.98 KBgambry
#39 interdiff-32-39.txt5.24 KBgambry
#32 3215062-32.patch185.08 KBquietone
#32 interdiff-28-32.txt4.3 KBquietone
#28 3215062-28.patch185.08 KBquietone
#28 interdiff-27.28.txt2.33 KBquietone
#27 3215062-27.patch184.88 KBquietone
#27 diff-23-27.txt1.84 KBquietone
#23 3215062-23.patch184.87 KBquietone
#23 interdiff-22-23.txt1.12 KBquietone
#22 3215062-22.patch184.67 KBdaffie
#22 interdiff-3215062-16-22.txt14.83 KBdaffie
#16 3215062-16.patch183.41 KBquietone
#16 interdiff-12-16.txt949 bytesquietone
#12 3215062-12.patch183.38 KBquietone
#12 interdiff-11-12.txt5.05 KBquietone
#11 3215062-11.patch179.17 KBquietone
#8 3215062-8.patch7.3 KBquietone
#8 interdiff-6-8.txt2.85 KBquietone
#6 3215062-6.patch6.81 KBquietone
#4 interdiff-2-4.txt4.96 KBquietone
#6 interdiff-4-6.txt2.52 KBquietone
#4 3215062-4.patch7.07 KBquietone
#2 3215062-2.patch5.74 KBquietone

Comments

quietone created an issue. See original summary.

quietone’s picture

Component: tracker.module » forum.module
Status: Needs work » Needs review
StatusFileSize
new5.74 KB
larowlan’s picture

This looks great, my only concern is that if we add an update test for each module it's going to increase the time it takes to run our test suite dramatically

Is there a way we can have one test, and each module adds its fixtures and asserts to that?

quietone’s picture

Title: Update forum module for Y2038 » Update forum and system module for Y2038
StatusFileSize
new4.96 KB
new7.07 KB

Yes, good idea. Let's keep the test in the system module. Therefore, I have added the changes from #3215067: Update system module for Y2038 which allows use to see how this will work for more y2038 changes.

quietone’s picture

Category: Task » Bug report
Issue tags: +Bug Smash Initiative

I think this qualifies as a bug.

Should we move the fixes for other modules here and fix this problem for more modules in one issue?

quietone’s picture

StatusFileSize
new2.52 KB
new6.81 KB

Use a foreach loop in the test since this is likely to be testing more tables.

larowlan’s picture

+++ b/core/modules/system/tests/src/Functional/Update/Y2038TimestampUpdate.php
@@ -0,0 +1,74 @@
+   * Tests update 9301.

I think this comment can probably be more generic now its testing multiple updates

Other than that, I think this is looking really good.

Should we postpone the other issues on getting this in so we can then build on top of this?

quietone’s picture

StatusFileSize
new2.85 KB
new7.3 KB

Thanks. Yes, I postponed the others earlier today. And there is a patch parked in the Meta, 65474#comment-41 for tables defined in core/lib.

I went to improv the comment as suggested in #7 and then proceeded to make other improvements to the test.

alexpott’s picture

Status: Needs review » Needs work

I think we should probably have 2 two issues for 2038 issue - one for \Drupal\Core\Field\Plugin\Field\FieldType\TimestampItem (ie. #2885413: Timestamp field items are affected by 2038 bug) and one for the rest which are changing hook_schema or pseudo hook_schema (i.e BatchStorage::schemaDefinition()) - I think per module here is probably the wrong scope and will introduce unnecessary inconsistency.

  1. +++ b/core/modules/forum/forum.install
    @@ -175,3 +175,30 @@ function forum_schema() {
    +function forum_update_9301(&$sandbox = NULL) {
    

    Need to change forum_schema() too.

  2. +++ b/core/modules/system/system.install
    @@ -1504,3 +1504,20 @@ function _system_advisories_requirements(array &$requirements): void {
    +function system_update_9301(&$sandbox = NULL) {
    

    Need to change system_schema() too.

quietone’s picture

Title: Update forum and system module for Y2038 » Update hook_schema for Y2038
Component: forum.module » base system
Assigned: Unassigned » quietone
Issue summary: View changes

Changing scope as per #9. I guess 'base system' is a more fitting component?

quietone’s picture

Issue summary: View changes
Status: Needs work » Needs review
StatusFileSize
new179.17 KB

Updated patch that merges the work in the now closed duplicate issues that were by module. Also, added this patch and work for the flood table. And there is a new dump that is based on 9.1.0 and that includes an enabled locale module.

I have not included an interdiff because there is so much new code added and bits and pieces have been reworked. It is essentially a new patch.

Still to do:
#9 1 and 2.

quietone’s picture

StatusFileSize
new5.05 KB
new183.38 KB

#9 1 and 2. All the *_schema files have been updated.

quietone’s picture

quietone’s picture

Assigned: quietone » Unassigned
larowlan’s picture

+++ b/core/modules/system/tests/src/Functional/Update/Y2038TimestampUpdateTest.php
@@ -0,0 +1,124 @@
+    $tables = $connection->schema()->findTables('migrate_map_%');
...
+    $tables = $connection->schema()->findTables('cache_%');

should we assert that both of these are not empty, so we know we have test coverage for at least one of these dynamic tables of each

I think the only task left here is to also ensure that we got all of them (manually I guess?)

quietone’s picture

Issue summary: View changes
StatusFileSize
new949 bytes
new183.41 KB

I have added an assertion that array of cache tables is not empty but not the map tables. I do not think it is necessary for the migrate_map table because they are not generated dynamically in this test, a sample map table is added in Y2038-timestamp.php, I don't see the need to do the extra work to create more map tables, they are all created with the same method \Drupal\migrate\Plugin\migrate\id_map\Sql::ensureTables so a sample should be fine.

Good point that this needs to be checked that all the timestamp schema have been found and changed. I recall doing variation of
grep -r 'function schemaDefinition' core and grep -r hook_schema core/modules. But it still requires a bit of research. I added this to the remaining tasks.

quietone’s picture

Priority: Normal » Critical

The parent issue was set to Critical before this was split to child issues. Marking this Critical as well.

daffie’s picture

Status: Needs review » Needs work
Issue tags: +Needs change record
  1. +++ b/core/modules/system/tests/src/Functional/Update/Y2038TimestampUpdateTest.php
    @@ -0,0 +1,125 @@
    +      DRUPAL_ROOT . '/core/modules/system/tests/fixtures/update/drupal-9.1.0.update.standard.php.gz',
    

    I am not saying that adding a new drupal 9.1 update standard is wrong, only could you explain why it is added and what is in it.

  2. +++ b/core/modules/system/tests/src/Functional/Update/Y2038TimestampUpdateTest.php
    @@ -0,0 +1,125 @@
    +        $result = $this->connection->query("SELECT data_type FROM information_schema.columns WHERE table_schema = '$this->databaseName' and table_name='$table_name' and column_name='$column_name';")
    +          ->fetchField();
    

    I think this only works for MySQL databases.

  3. I think we need a CR for module maintainers how to do the same for their module (contrib and custom). This should also include code examples.
daffie’s picture

Title: Update hook_schema for Y2038 » [PP-1] Update hook_schema for Y2038
Status: Needs work » Postponed
Related issues: +#301038: Add a cross-compatible database schema introspection API
+++ b/core/modules/system/tests/src/Functional/Update/Y2038TimestampUpdateTest.php
@@ -0,0 +1,125 @@
+        $result = $this->connection->query("SELECT data_type FROM information_schema.columns WHERE table_schema = '$this->databaseName' and table_name='$table_name' and column_name='$column_name';")
+          ->fetchField();

This 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.

daffie’s picture

Status: Postponed » Needs work

Talked to @catch on Slack about this and his suggestion was to not postpone this issue and specific code for each of the by core supported databases, because this is only in a test.

daffie’s picture

Title: [PP-1] Update hook_schema for Y2038 » Update hook_schema for Y2038
daffie’s picture

StatusFileSize
new14.83 KB
new184.67 KB

Updated the patch for the failing Y2038TimestampUpdateTest. SQLite does not to update as it is not affected. Therefor Y2038TimestampUpdateTest can be skipped by SQLite. As SQLite does not have information_schema to query. It has something else, only that is not necessary for this issue.

quietone’s picture

Issue summary: View changes
Status: Needs work » Needs review
StatusFileSize
new1.12 KB
new184.87 KB

#18.1. Added a comment to say what is installed in the fixture. Is that sufficient? I recall that when using drupal-9.0.0.bare.standard.php.gz there were errors about the post update hooks in language and locale. It was just easier to make a new fixture.

Also, tweaked the fixture so the site name is 'Drupal' and the admin user is 'admin'.

daffie’s picture

Issue summary: View changes
Issue tags: -Needs change record

@quietone: Thank you for explaining why the new fixture was added.

I have created a CR.

All my points from comment #18 are addressed.

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

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

gambry’s picture

Status: Needs review » Needs work

TBH I have mostly nitpicks:

  1. +++ b/core/modules/comment/comment.install
    @@ -116,3 +117,20 @@ function comment_schema() {
    +  if ($connection->schema()->tableExists('comment_entity_statistics') && $connection->databaseType() != 'sqlite') {
    

    I read and understood why this change has been made, but exactly because there is no difference between sqlite's integer sizes I would remove this checks on all schema updates. Removing the checks will help with code readability and - as already said - won't make any difference.

  2. +++ b/core/modules/system/tests/src/Functional/Update/Y2038TimestampUpdateTest.php
    @@ -0,0 +1,140 @@
    +      // enabled modules, forum, language, locale, statistics and tracker
    

    Typo enabled modules, -> enabled modules:

  3. +++ b/core/modules/system/tests/src/Functional/Update/Y2038TimestampUpdateTest.php
    @@ -0,0 +1,140 @@
    +      // modules enabled.
    

    We can get rid of this line, and just finish the one before with a full stop?

  4. +++ b/core/modules/system/tests/src/Functional/Update/Y2038TimestampUpdateTest.php
    @@ -0,0 +1,140 @@
    +    public function assertTimestampFields($expected_values) { ... }
    

    I would split this method in two, a collectTimestampFieldsFromDatabase(), filling in $this->table property, and leaving in assertTimestampFields() only the assertion bit.

quietone’s picture

Status: Needs work » Needs review
StatusFileSize
new1.84 KB
new184.88 KB

First, a reroll. Only changes were in system.install.

quietone’s picture

StatusFileSize
new2.33 KB
new185.08 KB

Ah, this adds an ignore line to fix the spelling from the reroll.

#28
1. Can we hear from @daffie before making this change? My two cents is to keep the existing code because we should only query the db when necessary. @daffie, can you comment?
2. Fixed
3. Fixed
4. New method added.

daffie’s picture

Issue summary: View changes
Status: Needs review » Needs work

For #28.1: SQLite does not need to change, because SQLite uses a more general dynamic type system. The size of the value determines the size that is used on disk. For more info: https://www.sqlite.org/datatype3.html.
We can remove the exceptions for SQLite, only the problem is that we need to test it and SQLite does not have "information_schema.columns" table/view. And yes it makes the patch a bit more cleaner, only the testing will be more difficult. I can live with how we are doing it now.

Back to needs work for:

+++ b/core/modules/comment/comment.install
@@ -116,3 +117,20 @@ function comment_schema() {
+function comment_update_9301(&$sandbox = NULL) {

This is not going in D9.3, therefor the function name should be: "comment_update_9401()". The same for all the other function names with "_9301" in it. I have updated the CR.

gambry’s picture

only the problem is that we need to test it and SQLite does not have "information_schema.columns" table/view

Hey @daffie, I'm with you with that. In fact my suggestion is to remove the check only on hook_schema() & Co., but keep it in that test.

Then yes, we should test SQLite as well. Why don't we change/extend the test so to INSERT a big int, we query it and we make sure has been stored correctly? Thoughts?

daffie’s picture

I would like to add SQLite support too. Only this change does nothing for SQLite. For SQLite there is just one integer type. Therefor I think that this is also untestable for SQLite. Before the running the update all time fields are integers and after the update this is still the same. Keeping the check in hook_update_N() is for me the slightly better option, because the change does no do anything for SQLite. I shall leave it to @quietone to decide to keep it or to remove it as she is the one who is making the patch.

quietone’s picture

Status: Needs work » Needs review
StatusFileSize
new4.3 KB
new185.08 KB

#29. This patch updated the hook number to 9401.

I am leaving the check in hook_update_N() as it still seems preferable to me to expose the difference in the database types. And there is still a committer to review, so it will get another look.

Status: Needs review » Needs work

The last submitted patch, 32: 3215062-32.patch, failed testing. View results

gambry’s picture

Status: Needs work » Reviewed & tested by the community

Thanks @daffie for the feedback and @quietone for the changes.

My worry about keeping the conditional check in hook_schema() & Co. is inconsistency in Drupal's schema when using different DB types. AKA exploring the schema metadata for MySQL/PgSQL will say Field X is a int:big while for SQLite will say Field X is int:normal. But I do understand for SQLite maps to the same storage is eventually the same, so no more objection.
Also I don't seem to be able to find a way to fetch the database - Drupal API - schema details for a field/table... so I'm worrying for nothing? :D :D

Moving this to RTBC. The failure on #32 is a glitch on FunctionalJavascript testing.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/modules/system/system.install
    @@ -1651,3 +1653,96 @@ function _system_check_array_table_prefixes() {
    +  $cache_tables = $schema->findTables('cache%');
    

    Why not cache_*

  2. +++ b/core/modules/system/tests/fixtures/update/Y2038-timestamp.php
    --- /dev/null
    +++ b/core/modules/system/tests/fixtures/update/drupal-9.1.0.update.standard.php.gz
    

    I don't understand we we're not using drupal-9.0.0.filled.standard.php.gz. If we use that dump then we only have to create the migrate_map_d7_file in core/modules/system/tests/fixtures/update/Y2038-timestamp.php. I think this is way better than maintaining another dump. This dump has batch and flood tables already and also has all the necessary modules enabled.

    Maintaining another db dump is not a free activity.

  3. +++ b/core/modules/system/tests/src/Functional/Update/Y2038TimestampUpdateTest.php
    @@ -0,0 +1,145 @@
    +    $tables = $connection->schema()->findTables('cache_%');
    +    $tables = array_diff($tables, ['cachetags']);
    

    cachetags will not be found when looking for cache_

alexpott’s picture

+++ b/core/modules/system/tests/src/Functional/Update/Y2038TimestampUpdateTest.php
@@ -0,0 +1,145 @@
+      $this->markTestSkipped();

We should have a helpful message here. For example

$this->markTestSkipped("This test does not support the SQLite database driver.");
alexpott’s picture

We're also going to need a 10.0.x version of the patch as well.

gambry’s picture

gambry’s picture

Status: Needs work » Needs review
StatusFileSize
new5.24 KB
new24.98 KB

This patch addresses feedback from #35.2 deleting the new fixture and using 9.3.0.filled.
Also since #35.1 and #35.3 can't be addressed due #3269091: Undocumented behaviour for Schema::findTables() when an underscore is used I made the code consistent, ready for when that issue is fixed and adding TODOs so we remove the array_diff($tables, ['cachetags']) lines when the time comes.

I'm not a big fan of TODOs, but since this one is critical and the related issue is not I'm leaning towards allowing the TODO.

Still needs re-roll for 10.0.x

gambry’s picture

Status: Needs review » Needs work
StatusFileSize
new25 KB

This is an as-is reroll of #39, but for 10.0.x. So this include the CS issues #39-9.4.x has got as well.

Leaving Needs Work to fix the CS issues.

gambry’s picture

Status: Needs work » Needs review
StatusFileSize
new1.51 KB
new24.98 KB
new1.51 KB
new25 KB

This is #39 (and #40) patches with he IS fixed.

alexpott’s picture

Status: Needs review » Needs work

@gambry nice work on filing #3269091: Undocumented behaviour for Schema::findTables() when an underscore is used

+++ b/core/modules/system/system.install
@@ -1651,3 +1653,98 @@ function _system_check_array_table_prefixes() {
+  // @todo Remove/review the line below when following issue is fixed
+  //   https://www.drupal.org/project/drupal/issues/3269091
+  $cache_tables = array_diff($cache_tables, ['cachetags']);

+++ b/core/modules/system/tests/src/Functional/Update/Y2038TimestampUpdateTest.php
@@ -0,0 +1,147 @@
+    $tables = $connection->schema()->findTables('cache_%');
+    // @todo Remove/review the line below when following issue is fixed
+    //   https://www.drupal.org/project/drupal/issues/3269091
+    $tables = array_diff($tables, ['cachetags']);

I think we need to remove all entries not starting with cache_ - just in case a contrib or custom module has added something similar to cachetags. Something like

$tables = array_filter($tables, function ($table) { return str_starts_with($table, 'cache_');});

I think the solution to #3269091: Undocumented behaviour for Schema::findTables() when an underscore is used is to improve the documentation.

ravi.shankar’s picture

Status: Needs work » Needs review
StatusFileSize
new25.06 KB
new1.63 KB
new25.07 KB
new1.63 KB

Made changes as per comment #42.

gambry’s picture

Status: Needs review » Needs work

There are CS issues to be fixed. Also I noticed change on #36 still needs to be addressed.

yogeshmpawar’s picture

Status: Needs work » Needs review
StatusFileSize
new25.14 KB
new2.11 KB

CS issues fixed & addressed #36

gambry’s picture

Status: Needs review » Needs work

Amazing, I can see feedback from #42 ad #36 has been addressed, as well as CS issues from #43.

The only missing bit is rolling the same fixes on #41-10.0.x patch. So setting needs work for this reason.

gambry’s picture

Also hiding some files not needed anymore.

ravi.shankar’s picture

Status: Needs work » Needs review
StatusFileSize
new25.15 KB
new2.11 KB

Added a patch for Drupal 10 with fixes of comment #44.

gambry’s picture

Status: Needs review » Needs work

I reviewed the patch thoroughly and all looks great!

However... one more thing:

+++ b/core/modules/system/system.install
@@ -1569,3 +1571,100 @@ function _system_advisories_requirements(array &$requirements): void {
+  // @todo Remove/review the line below when following issue is fixed
+  //   https://www.drupal.org/project/drupal/issues/3269091

+++ b/core/modules/system/tests/src/Functional/Update/Y2038TimestampUpdateTest.php
@@ -0,0 +1,149 @@
+    // @todo Remove/review the line below when following issue is fixed
+    //   https://www.drupal.org/project/drupal/issues/3269091

I believe we can now remove these TODOs.

ravi.shankar’s picture

Status: Needs work » Needs review
StatusFileSize
new24.88 KB
new1.4 KB
new1.4 KB
new24.89 KB

Addressed comment #49.

gambry’s picture

Status: Needs review » Reviewed & tested by the community

Nice work everyone. I presume this can be RTBC now.

The only thing I'm not be able to assess is how long the update(s) will take for big datasets. I know I tried this somewhere else, but I can't find where anymore. :(
The other face of the problem is: I don't think there is a lot we can do. We can't batch column size updates, so even though that will be slow - if it will be - we can't avoid it. But maybe we can advice better? So does the Change records need a line about it?

mfb’s picture

Status: Reviewed & tested by the community » Needs review

str_starts_with() is a really nice function but I don't think it can be used on Drupal 9.4, as it's a PHP 8 function. Unless the minimum PHP version is going to change?

Maybe instead, escapeLike() can be called to escape the underscore (assuming that's the problem here, I didn't look into how findTables() works).

mfb’s picture

re: #52 I might have lied, I see that core uses symfony/polyfill-php80 so str_starts_with() should be ok to use?

I tested the update on a small-ish MySQL database and it looked like most of the time was spent updating the cache tables (which had around 5000 rows), which had to be copied to temporary table while altering. Perhaps this update could be optimized by truncating the cache tables before altering, rather than the current situation where they are altered only to be truncated later?

mfb’s picture

We can't batch column size updates

Actually these probably need to be batched using $sandbox? Although, if the cache tables are truncated first, perhaps the alter statements are fast enough to not be batched.

gambry’s picture

Actually these probably need to be batched using $sandbox?

I don't think that will help. We can batch the $connection->schema()->changeField() command, but since the long part will be waiting for DBs to apply the change batching PHP side may be quite useless.

I tested the update on a small-ish MySQL database and it looked like most of the time was spent updating the cache tables

Can you define what kind of timeframe are we talking about? Seconds? Minutes?

Perhaps this update could be optimized by truncating the cache tables before altering

Possibly. I'm not sure what policies are around truncating cache within hooks_update(), but I personally don't see any downside.

Also since most high traffic instances may use memcache/redis for cache tables, do we need to anything around that?

@mfb if you can report timings around running the updates, I'll gather attention around the cache_ tables topics.

gambry’s picture

+++ b/core/modules/system/system.install
@@ -1569,3 +1571,98 @@ function _system_advisories_requirements(array &$requirements): void {
+  $cache_tables = $schema->findTables('cache_%');
+  $cache_tables = array_filter($cache_tables, function ($cache_tables) {
+    return str_starts_with($cache_tables, 'cache_');
+  });
+  if ($connection->databaseType() != 'sqlite') {
+    foreach (array_keys($cache_tables) as $table) {
...
+    }
+  }

We don't need to worry about memcache/redis. In this lines we look for any 'cache_' table in the database, and for each one we find we apply the changes. That means the actual memcache/redis table are untouched (and it's not core's problems to make sure they support 2038 dates).
However this code will run if cache_* tables exists in the database, so flushing the cache will not flush the unused db tables.

mfb’s picture

On my instance, the cache_page table size on disk was 168MB and changeField() took 13 seconds; cache_dynamic_page_cache was 116MB and changeField() took 9 seconds; i.e. 13 megabytes per second or 79 seconds per gigabyte.

We can batch the $connection->schema()->changeField() command, but since the long part will be waiting for DBs to apply the change batching PHP side may be quite useless.

Yes, I was imagining a hypothetical case of a hosting environment that has a request timeout and the site has at least a couple large tables to alter; if the table alter statements are setup as batch operation then there is much less concern with hitting the timeout. I've seen some long-running database migrations use hook_update_N($sandbox) in the past, but no idea what the criteria was for doing so - just wanted to flag this for review.

longwave’s picture

  1. +++ b/core/modules/dblog/dblog.install
    @@ -97,3 +98,20 @@ function dblog_schema() {
    +    $connection->schema()->changeField('watchdog', 'timestamp', 'timestamp', $new);
    

    This is the change I would worry about the most, I have seen sites that never clear watchdog and have multi-gigabyte sized tables as a result.

  2. +++ b/core/modules/system/tests/src/Functional/Update/Y2038TimestampUpdateTest.php
    @@ -0,0 +1,147 @@
    +      // Start with a standard install of Drupal 9.1.0 with the following
    +      // enabled modules: forum, language, locale, statistics and tracker.
    +      DRUPAL_ROOT . '/core/modules/system/tests/fixtures/update/drupal-9.3.0.filled.standard.php.gz',
    

    9.1.0 -> 9.3.0 in the comment

mfb’s picture

This is the change I would worry about the most, I have seen sites that never clear watchdog and have multi-gigabyte sized tables as a result.

Well, this module has just one changeField() call in the update function, so there isn't much that can be done, right? But if there are multiple changeField() calls in an update function then batch operation could be used.

longwave’s picture

Discussed with @gambry in Slack. There is no guaranteed way of preventing timeout in some cases, as we just cannot know how large some tables might be, nor how slow some database operation might be as it is hardware dependent.

The change reminded me of the utf8 to utf8mb4 switch in Drupal 7 (see https://www.drupal.org/node/2754539) where we opted in new sites by default but existing sites were given instructions and a contrib module to help them upgrade. This is an alternative option here - albeit more complicated to implement and support - where in a similar fashion we opt in new sites to Y2038 compliance by default via a switch in settings.php, and allow other sites to upgrade in their own time.

However, timeouts should not cause data loss; the schema change will complete in the background eventually and once this is done a retry is a no-op so as long as this is documented then users who experience a timeout in update.php or drush updb should be able to safely retry until the operations are all completed.

Regarding the cache tables I don't see an issue with truncating those beforehand as database updates invariably end in a cache clear anyway, so it saves us time if we can wipe that first, but there is still the issue of tables that contain persistent data that might be very large.

gambry’s picture

Status: Needs review » Needs work

Let's Needs work in order to address #58.1 and have a look if we need to worry about PostgreSQL error on #50.

The documentation mentioned on #60 can be included in the Change Record? Since the schema change is in multiple places, and there are other issues tackling Y2038 schema changes from other perspectives, having this in code in a single place may be problematic.

Adding the documentation depends on someone to try this update in a big dataset. I'll try myself, but I don't have a Drupal big db so I need to create one. If a gentle soul has got one that would save me time and bandwidth. 🙏🏻🙏🏻

catch’s picture

Quick timings running the updates on a fairly large db. comment_entity_statistics has 56k rows. At least one migrate_map table with half a million rows.

No cache tables (agreed we could truncate those before running the update).

time drush updb -y
 --------- ----------- --------------- ---------------------------------- 
  Module    Update ID   Type            Description                       
 --------- ----------- --------------- ---------------------------------- 
  system    9401        hook_update_n   9401 - Remove the year 2038 date  
                                        limitation.                       
  comment   9401        hook_update_n   9401 - Remove the year 2038 date  
                                        limitation.                       
  dblog     9401        hook_update_n   9401 - Remove the year 2038 date  
                                        limitation.                       
  history   9401        hook_update_n   9401 - Remove the year 2038 date  
                                        limitation.                       
  migrate   9401        hook_update_n   9401 - Remove the year 2038 date  
                                        limitation.                       
 --------- ----------- --------------- ---------------------------------- 


 // Do you wish to run the specified pending updates?: yes.                   

>  [notice] Update started: system_update_9401
>  [notice] Update completed: system_update_9401
>  [notice] Update started: comment_update_9401
>  [notice] Update completed: comment_update_9401
>  [notice] Update started: dblog_update_9401
>  [notice] Update completed: dblog_update_9401
>  [notice] Update started: history_update_9401
>  [notice] Update completed: history_update_9401
>  [notice] Update started: migrate_update_9401
>  [notice] Update completed: migrate_update_9401
 [success] Finished performing updates.

real	0m16.834s
user	0m2.211s
sys	0m0.394s

That seems fine to me tbh.

  1. +++ b/core/modules/migrate/migrate.install
    @@ -11,3 +11,24 @@
    +
    +/**
    + * Remove the year 2038 date limitation.
    + */
    +function migrate_update_9401(&$sandbox = NULL) {
    +  $connection = \Drupal::database();
    +  $tables = $connection->schema()->findTables('migrate_map_%');
    +  if (!empty($tables) && $connection->databaseType() != 'sqlite') {
    +    foreach ($tables as $table) {
    +      $new = [
    

    It would theoretically be possible to batch these, possibly not necessary given the timings.

  2. +++ b/core/modules/system/system.install
    @@ -1569,3 +1571,98 @@ function _system_advisories_requirements(array &$requirements): void {
    +
    +  // Update cache tables.
    +  $cache_tables = $schema->findTables('cache_%');
    +  $cache_tables = array_filter($cache_tables, function ($cache_tables) {
    +    return str_starts_with($cache_tables, 'cache_');
    +  });
    +  if ($connection->databaseType() != 'sqlite') {
    +    foreach (array_keys($cache_tables) as $table) {
    +      $new = [
    +        'description' => 'A Unix timestamp indicating when the cache entry should expire, or ' . Cache::PERMANENT . ' for never.',
    

    Does this need to be concerned about the cache_tags table (which isn't a cache table as such).

    Also this system update could be split into multiple sequential updates without having to add any batch logic if we wanted.

alexpott’s picture

Re #62.2 it's called cachetags and we're removing it from the list of tables to process because it does not have a date... it just has an invalidations counter. The fun thing is that it is returned by $schema->findTables('cache_%'); because of #3269091: Undocumented behaviour for Schema::findTables() when an underscore is used.

mfb’s picture

Documenting that sites can clear caches and database log to speed up the update should help here. How long the table alter takes mainly depends on size of the table not number of rows - a site with large comment_entity_statistics table could easily have larger cache tables, but they have the ability to clear them first.

alexpott’s picture

@mfb I think it is reasonable for the system_update_9401() to truncate the cache tables prior to changing them. They are going to be truncated later anyway.

gambry’s picture

While working on a patch to address #58.2 and truncating the cache tables on system_update_9401() I noticed all cache tables have the created column using signed DECIMAL(14,3) for timestamp with microseconds.

On MySQL DECIMAL(14,3) allow dates up to 5138-11-16 09:46:39 (date('Y-m-d H:i:s', 99999999999);). Although smaller than BIGINT range, I'm confident we are safe.
I'll check on PostgreSQL just to be safe, but I'm almost sure the range will be pretty much the same.

gambry’s picture

Status: Needs work » Needs review
StatusFileSize
new25.13 KB
new25.14 KB
new1.67 KB
new1.67 KB

Here we go. Patches for 10.0.x and 9.4.x addressing #58.2 and truncating the cache tables in system_update_9401().

gambry’s picture

On MySQL DECIMAL(14,3) allow dates up to 5138-11-16 09:46:39 (date('Y-m-d H:i:s', 99999999999);)

BTW, same for PostgreSQL ans SQLite. So all good.

The last submitted patch, 67: 3215062-79--9.4.x.patch, failed testing. View results

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

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

aarti zikre’s picture

Assigned: Unassigned » aarti zikre

will review this

aarti zikre’s picture

Assigned: aarti zikre » Unassigned
Status: Needs review » Reviewed & tested by the community
StatusFileSize
new79.51 KB
new25.74 KB
new82.89 KB

Verified both the patches for drupal 9.5.x and drupal 10.
https://www.drupal.org/files/issues/2022-04-29/3215062-79--9.4.x.patch
https://www.drupal.org/files/issues/2022-04-29/3215062-79--10.0.x.patch

Testing Steps for Drupal 9:
* Install new Drupal Instance.
* Apply patch for Drupal 9.5
https://www.drupal.org/files/issues/2022-04-29/3215062-79--9.4.x.patch
* Run Update
drush updb

Testing Steps for Drupal 10:
* Install new Drupal Instance.
* Apply patch for drupal 10
https://www.drupal.org/files/issues/2022-04-29/3215062-79--10.0.x.patch
* Run Update
drush updb

Refer SS:
Drupal 9.5.x drush output:
2022-07-19/3215062 afterpatch.png

Drupal 9.5.x flood table timestamp field declared as bigint:
2022-07-19/3215062 after patch db.png

Drupal 10 drush output:
2022-07-19/3215062 after patch d10.png

Note: I have used lando setup and mysql database.

Test Result: Pass
Can be move to RTBC

catch’s picture

Version: 9.5.x-dev » 10.1.x-dev
Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs work
+++ b/core/modules/comment/comment.install
@@ -116,3 +117,20 @@ function comment_schema() {
+
+/**
+ * Remove the year 2038 date limitation.
+ */
+function comment_update_9401(&$sandbox = NULL) {
+  $connection = \Drupal::database();
+  if ($connection->schema()->tableExists('comment_entity_statistics') && $connection->databaseType() != 'sqlite') {
+    $new = [

We're trying to avoid committing schema updates to 9.4.x or 9.5.x at this point, since it's an extra potential thing that can go wrong when updating from 9.4 to 10.0.

Patch therefore should be re-rolled against 10.1.x (i.e. comment_update_10100(), the first five-digit update number!).

A bit painful pushing this back a release but every time we have schema changes close to a major update something always goes wrong. And the patch can be committed to 10.1.x at any point from now onwards.

dww’s picture

Status: Needs work » Needs review
Issue tags: -Needs work
StatusFileSize
new25.15 KB
new4.31 KB

How's this? Interdiff relative to #67, even though that patch was numbered as "79".

Status: Needs review » Needs work

The last submitted patch, 74: 3215062-73--10.1.x.patch, failed testing. View results

dww’s picture

Status: Needs work » Needs review
StatusFileSize
new25.01 KB
new919 bytes

Ugh, sorry. My local is busted right now for running 10.* tests. 😢 Let's hope the bot is happy with this one...

gambry’s picture

Status: Needs review » Reviewed & tested by the community

even though that patch was numbered as "79"

::facepalm::

I know I worked on the original patch, but my bit has been reviewed by @aarti zikre on #72 and @catch on #73. The rework on #76 is simply to address the rolling of the patch against 10.1.x.

Hopefully I won't get told off due RTBCing this :D

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed 194b56c and pushed to 10.1.x. Thanks!

Getting this in nice and early will at least allow as much time as possible for any gremlins to be found.

  • alexpott committed 194b56c on 10.1.x
    Issue #3215062 by quietone, gambry, ravi.shankar, dww, daffie,...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

mandclu’s picture