Problem/Motivation
This is an issue that we haven't had to deal with since the Drupal 6 to Drupal 7 upgrade path, but now we do again.
The following scenarios are broken:
hook_update_N()
system_update_100000()
is added to 11.0.x and 10.3.x
[time elapses]
system_update_100100()
is added to 11.1.x to support a new feature, it is not backported to 10.4.x
[time elapses]
A critical bug fix which is eligible for backport to 10.4.x, but requires a small update, is committed to 11.1.x
[dragons emerge from the darkness, volcanos erupt]
If this update is added as system_update_100101()
and backported to 10.4.x, then when 10.4.x sites are updated to 11.1.x, they won't run system_update_100100() because their schema version will be higher already.
If this update is added as system_update_100001() in 11.1.x, and 10.4.x
, it won't run on 11.x sites because their schema version will be 100100
already.
hook_post_update_NAME()
This is less of an issue, but still a problem:
system_post_update_fix_config() is added to 11.1.x, it is backported to 10.4.x, but not 11.0.x because it's security-only.
If a site updates from 10.4.x to 11.0.x, then the post update goes 'missing' from the code base. It will then re-emerge when the site updates again to 11.1.x or 11.2.x
Sequential backports to minor branches without skipping a release are OK though for post updates regardless of how far they go back, since they don't interfere with other post updates.
Proposed resolution
This is only a serious problem for hook_update_N() due to sequential update ordering. Because post updates are tracked individually and order isn't guaranteed, the only issue with those is a site updating from 10.4 to 11.0 (i.e. 'backwards' in terms of minor release parity).
For hook_update_N(), we can add a small API addition to the update system, which will probably be exclusively used by core updates.
1. An 11.x hook_update_N() would be added as normal, with no extra method calls or metadata. Let's say system_update_11100()
2. When that same update is backported to 10.x, we backport it with it's own update number, say system_update_10400().
In system_update_10400(), we call \Drupal::service('update.update_hook_registry')->skipUpdate(11100)
This logs the module, update being run, and future update, to a key value collection.
When a site updates to 11.1 from 10.4, it will have this information in key value. In update_do_one() we then check against the key value collection, and if an 11.1 update should be skipped, we skip it and log a success message.
The huge advantage of this is that there is no 11.x-specific code dealing with the 10.4.x update numbers, we just need the API in place to check key/value and that's it. 11.x updates can be committed with no API changes. 10.4 update backports need to call one method with a small amount of information.
If a site tries to update to 11.0 from 10.4, then in terms of database updates it will be going 'backwards' - however because we have the key value store, we can detect this, and issue an update requirements error telling them to go to 11.1 instead.