Problem/Motivation

We placeholder comment links which causes them to get built on every page view. This is likely unnecessary for the same reason as #2556767: Remove placeholdering of node links.

Found during profiling at #2552873: node/1 flamegraphs. See flame graph #1. This may be critical - we wont know much savings we'll get until after a patch is made (see #1744302: [meta] Resolve known performance regressions in Drupal 8).

Proposed resolution

Remaining tasks

User interface changes

API changes

Data model changes

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

moshe weitzman created an issue. See original summary.

plach’s picture

moshe weitzman’s picture

No - this has nothing to do with forms. The two issues sound similar but aren't.

Wim Leers’s picture

Status: Active » Needs review
Related issues: +#2556767: Remove placeholdering of node links
FileSize
583 bytes

This is the basic change we need.

Let's see what fails.

Wim Leers’s picture

FileSize
5.06 KB
4.52 KB

This ensures the necessary cacheability metadata is present.

Wim Leers’s picture

Note that this needs #2579021: Prevent comment forms from marking rendered nodes as uncacheable to be in.

Without that patch, the entire rendered node is not cached, because of the comment form's CSRF token's max-age=0 that is bubbled. If you apply both that patch and this patch, you'll see render cache entries like entity_view:node:1:full:* again, and you'll see that without this patch, the comment links are placeholders, and with this patch, they are included right in the node's render cache entry (one set of comment links per comment on the node), meaning they aren't rendered after everything else anymore.

Finally, for clarify, let me repeat part of what I wrote at #2556767: Remove placeholdering of node links, but adjusted for this issue. So great to see that almost two years after #2090783: Run comment op links (delete, edit, reply, approve + contrib) through #post_render_cache to prevent render caching granularity being per-user, we are now able to simplify it enormously thanks to #2429257: Bubble cache contexts :)

Marking those as related issues to simplify future archeological work.

Wim Leers’s picture

#4 unsurprisingly shows that we have almost no test coverage for this. #2090783: Run comment op links (delete, edit, reply, approve + contrib) through #post_render_cache to prevent render caching granularity being per-user added some test coverage (specifically to check that altering in additional comment links actually works), but ideally we'd have more.

Feel free to contribute test coverage.

Status: Needs review » Needs work

The last submitted patch, 5: 2579287-5.patch, failed testing.

Wim Leers’s picture

Status: Needs work » Needs review
FileSize
5.95 KB
1.79 KB

One less fail.

Status: Needs review » Needs work

The last submitted patch, 9: 2579287-9.patch, failed testing.

plach’s picture

Oh, links, I misread the title, sorry.

The last submitted patch, 5: 2579287-5.patch, failed testing.

The last submitted patch, 9: 2579287-9.patch, failed testing.

Wim Leers’s picture

Status: Needs work » Needs review
Related issues: +#2478483: Introduce placeholders (#lazy_builder) to replace #post_render_cache
FileSize
8.04 KB
2.14 KB
+++ b/core/includes/common.inc
@@ -856,9 +856,11 @@ function drupal_pre_render_links($element) {
-    // Merge attachments.
-    if (isset($child['#attached'])) {
-      $element['#attached'] = BubbleableMetadata::mergeAttachments($element['#attached'], $child['#attached']);
+    // Merge bubbleable metadata.
+    if (isset($child['#cache']) || isset($child['#attached'])) {
+      BubbleableMetadata::createFromRenderArray($element)
+        ->merge(BubbleableMetadata::createFromRenderArray($child))
+        ->applyTo($element);
     }

This change causes test failures in StandardTest.

Why this change? To bubble the cacheability metadata from individual hook_comment_links_alter() implementations. And consequently, also for hook_node_links_alter(). Yes, drupal_render_pre_links() is very strange, and it is optimized specifically for hook_(comment|node)_links_alter()'s needs. Without this change, the cacheability metadata of the additions/alterations made by those hooks are lost, and therefore things are varied insufficiently.

Why this change causes a failure? Because node links (and thus also links added by hook_node_links_alter()) are already no longer being placeholdered always, since #2556767: Remove placeholdering of node links. And comment_node_links_alter() (the comment.module implementation of hook_node_links_alter()) is specifying cacheability metadata, which was being lost. No specific test coverage exists for this, because until #2556767: Remove placeholdering of node links, this was always being placeholdered, i.e. was always being rendered late, i.e. was never cached, and hence we never considered it necessary to write explicit test coverage for the node links.

Since #2478483: Introduce placeholders (#lazy_builder) to replace #post_render_cache, comment_node_links_alter() is specifying the user cache context, which is the precise reason for this test failure. Since that issue the individual links are not placeholdered anymore, because the overall links were already being placeholdered. #2556767 then changed that, but failed to make the code change quoted above, so actually, the per-user "last read data" is being cached as part of nodes right now.


This reroll fixes the fail in StandardTest. It lets us once again rely on the JS that is responsible for rendering this anyway. Since that JS then no longer receives the data it needs to do the client-side rendering, it instead needs to do a request to the server. But the results of that are cached on the client-side anyway.

Wim Leers’s picture

FileSize
8.74 KB
767 bytes

Fixing the last fail.

Wim Leers’s picture

Note that #14 is an important bugfix that should go in regardless of this issue — it can be done in a separate issue if we want.

Wim Leers’s picture

Finally, let's prove the full node page is still cacheable by the Dynamic Page Cache.

We actually had a test for this, but #2511516: Make local tasks and actions hooks provide cacheability metadata, to make the blocks showing them cacheable commented it out, and it should've been uncommented in #2543334: Auto-placeholdering for #lazy_builder with bubbling of contexts and tags, but that was forgotten, because both were in progress simultaneously. Uncommenting that test shows that Dynamic Page Cache considers the full node page UNCACHEABLE. Uh-oh.

But actually, we already kinda know why — see the #9 interdiff:

+++ b/core/modules/comment/src/Tests/CommentCacheTagsTest.php
@@ -139,7 +139,7 @@ public function testCommentEntity() {
+    return ['user', 'user.roles:authenticated'];

The edit own comments permission is causing the comment links to be cacheable per user. Therefore the comment links still vary by user.

Next, because entities are not yet rendered using #lazy_builder (they're still rendered using #pre_render), they cannot yet be auto-placeholdered. If they used the #lazy_builder pattern, this patch would cause all commented entities to be auto-placeholdered because of that edit own comments permission. Of course, that also wouldn't help us at all: we'd then be rendering the entire commented entity including all comments and all comment links on every page load… which is even worse than the problem this issue tries to solve (avoiding rendering all comment links on all page loads).

And so because the user cache context bubbles up to the page in the current patch, that means that the full node page can not be cached by Dynamic Page Cache.

But it's quite clear that due to the edit own comments permission, this can never work in a performant way, unless we defer the rendering of the "edit" comment link to the client-side. But that's quite a bigger undertaking.

Conclusion: I don't think it actually makes sense what the issue title says. The "edit" comment link unfortunately gets very much in the way.

Wim Leers’s picture

FileSize
9.97 KB
1.28 KB

Finally, let's prove the full node page is still cacheable by the Dynamic Page Cache.

We actually had a test for this, […]

This reroll gives us that test. Per the explanation in #17, this will fail.

The last submitted patch, 15: 2579287-15.patch, failed testing.

The last submitted patch, 14: 2579287-14.patch, failed testing.

The last submitted patch, 14: 2579287-14.patch, failed testing.

The last submitted patch, 15: 2579287-15.patch, failed testing.

Wim Leers’s picture

Title: Stop placeholdering comment links - render cache instead » Follow-up for #2556767: node links are missing 'user' context, and caching uncacheable data
Component: comment.module » node system
Issue tags: -flame +Needs tests
FileSize
7.83 KB
2.24 KB

BUT! Most of the work done for this issue is still important/relevant. Let's look at each hunk in #18:

  1. common.inc: this causes the actual cacheability metadata for node links to be bubbled, see explanation in #14
  2. CommentLazyBuilders: it is better (but not essential) for placeholdered content to provide correct cacheability metadata
  3. CommentLinkBuilder: this is currently a bug in how nodes are cached, ever since #2556767: Remove placeholdering of node links landed, in two ways even: A) the history metadata becomes stale very quickly, for it is not invalidated by cache tags, B) the history metadata is actually per user, and it has the user cache context but in HEAD (see point 1), that cacheability metadata is discarded/ignored, so we cache user-specific data across users. This is wrong (the "X new comment" counts will be for a different user). The hunk in point 1 fixes that, but causes nodes to be cached per user. That's also very bad. So this hunk is necessary to keep nodes cacheable across users.
  4. CommentViewBuilder: was for this issue, can be discarded, because this issue is impossible to fix while we have the "edit own comments" permission, see #17.
  5. CommentCacheTagsTest: was for the previous point, can be discarded.
  6. CommentDefaultFormatterCacheTagsTest: was also for the previous point, can also be discarded.
  7. StandardTest: brings back important test coverage proving that the full node page is indeed still cacheable by Dynamic Page Cache, important combined with the other hunks that are important to keep.

So, repurposing this issue for those things. Patch is the same as #18, but with irrelevant hunks removed (see points 4, 5 and 6 above).

Wim Leers’s picture

Ok, so as the CommentNewIndicatorTest failure in #14 and #15 shows, we do have some test coverage for CommentLinkBuilder's logic (that we're changing since #14). That's great. We'll still need to fix that test for the patch in #23, but all the things I wrote in the past few comments are still all true.

Wim Leers’s picture

Issue tags: -Needs tests
FileSize
9.61 KB
1.83 KB

This does what I said in #24.

The last submitted patch, 18: 2579287-18.patch, failed testing.

The last submitted patch, 18: 2579287-18.patch, failed testing.

The last submitted patch, 23: 2579287-19.patch, failed testing.

The last submitted patch, 23: 2579287-19.patch, failed testing.

kylebrowning’s picture

+++ b/core/profiles/standard/src/Tests/StandardTest.php
@@ -193,11 +193,10 @@ function testStandard() {
+    $this->drupalGet($url);
+    $this->drupalGet($url);

Does this need to happen twice?

kylebrowning’s picture

I suppose it does to prove that you hit the cache.

Nothing to see here!

moshe weitzman’s picture

The node system also has 'edit own [content type] perm' but that link manifests as a local task, not as a node link so thats why node links are cacheable but not comment links. Poor comment module can never catch a break.

Maybe someone can find a way to speed up comment link rendering now that it looks uncacheable. Please click on Drupal\comment\CommentLazyBuilders::renderLinks (see far right) when viewing this flame graph.. I'll start off:

Why is Drupal\comment\Entity\Comment::getCommentedEntity so wide? That method loads the node associated with the current comment. We have static and persistent caching for nodes so this should be fast. I stepped through and didnt see any red flag.

pwolanin’s picture

Priority: Critical » Major

If it's not a security issue or causing data loss, it shouldn't be critical.

YesCT’s picture

Issue summary: View changes

I was wondering also if this was critical. https://www.drupal.org/core/issue-priority

putting the template in the summary, that should help (as it has a place to fill in if there would be api changes and data model changes)

note, the original report said "Marking as critical just to be conservative. I don't actually think this needs to block RC1." which supports the not being critical move.

catch’s picture

I think this is something we'd commit to a patch release (unless I missed something) so agreed on major.

Wim Leers’s picture

I agree; this is a pure bugfix since #23, so can go in after RC1, e.g. for RC2.

Wim Leers’s picture

Issue tags: -Performance

The "Performance" issue tag is also no longer applicable since #23.

Fabianx’s picture

As already spoken in IRC:

The best way out is to make comment rendering itself a lazy builder - the whole block - BUT also add the same cache keys as the node.

Then the node is cacheable and the page with blocks is cachable, but the comments and comment links are rendered and cached independently - e.g. per user.

This also makes a lot of sense going forward when thinking of BigPipe / streaming styling things, the most important things first, then load the comments later - which is already the case on 90% of the pages e.g. using disqus.

Another maybe less invasive change is to just add #cache keys to the comment links, but keep the #create_placeholder.

Fabianx’s picture

There is another trick we can do here ;).

We can use #lazy_builder, which generates #pre_render + #cache in a child element.

Then the cache keys could be dynamically generated by the lazy builder based on e.g. the owner vs. current uid information.

Of course it would only work for the standard cases we know about and a new link could still make it cacheable per user only.

But it would work ... (the same as comments are render cached on drupal.org now)

Wim Leers’s picture

#39: we can't use #lazy_builder for comment links because of the way drupal_pre_render_links() and #theme => links work. At least not last time I looked.

Wim Leers’s picture

The other idea I had, explained at a high (not 100% accurate) level: every comment has a data-comment-user-id attribute. We could match that up with drupalSettings.user.uid, and then only show the edit link if the user has the edit own comments permission.
EDIT: but obviously this immediately breaks down when you have custom access control for comments, e.g. group-based. I was just posting the idea, perhaps it'll inspire a better solution :)

catch’s picture

One idea I had which would handle the placeholdering for us was reversing what we did on #611642: Roll back comment contextual links (temporarily). However we'd need some kind of fallback for sites that don't have contextual module enabled then.

Wim Leers’s picture

Assigned: Unassigned » Wim Leers
Status: Needs review » Needs work

catch had a great insight that allows us to still address the original issue. Currently working on that. But first lunch.

Wim Leers’s picture

Title: Follow-up for #2556767: node links are missing 'user' context, and caching uncacheable data » Stop placeholdering comment links
Component: node system » comment.module
Assigned: Wim Leers » Unassigned
Status: Needs work » Needs review
Issue tags: +flame
FileSize
12.83 KB
3.33 KB

So the simple yet genius insight is this:

-        return AccessResult::allowedIf($account->id() && $account->id() == $entity->getOwnerId() && $entity->isPublished() && $account->hasPermission('edit own comments'))->cachePerPermissions()->cachePerUser()->cacheUntilEntityChanges($entity);
+        // If the user doesn't have the permission to edit their own comment,
+        // return early with better cacheability.
+        if (!$account->hasPermission('edit own comments')) {
+          return AccessResult::neutral()->cachePerPermissions();
+        }
+        // Otherwise, if they do have the permission, do the further necessary
+        // checks, at the cost of poorer cacheability.
+        return AccessResult::allowedIf($account->id() && $account->id() == $entity->getOwnerId() && $entity->isPublished())->cachePerPermissions()->cachePerUser()->cacheUntilEntityChanges($entity);
Wim Leers’s picture

For #42:

One idea I had which would handle the placeholdering for us was reversing what we did on #611642: Roll back comment contextual links (temporarily). However we'd need some kind of fallback for sites that don't have contextual module enabled then.

I've opened #2580407: Consider using contextual links for Comments again with an initial patch, against 8.1.

moshe weitzman’s picture

I'm a little wary of performance of contextual solution (and UX). Could be trading snails. My fav option is the javascript solution form #41. Group based comment access control is highly unusual (I've never seen it). I think such a solution could reasonably implement own 'edit own comment' feature if needed.

Berdir’s picture

The JS based solution sounds interesting but I don't see how we can implement that in a practical timeframe. This is all fairly generic code that relies on entity access, which in turn has hooks that you can implement or even replace the access control handler completely. We can't just hardcode some assumptions there, now.

Group based access might not be common, but time based edit access, that only allows edit access for a few hours or until you have responses are not that uncommon and will be implemented by sites (stackexchange does the time thing, for example).

On #44, That only helps if you have no role with that permission. One user accessing a page with that permission is enough to force that cache context for all users, as they are merged together. Unfortunately, we have no way to specific something like "cache by permissions, unless it's *that* specific permission hash, then it is per user". I don't even want to imagine how complicated that would become ;)

Fabianx’s picture

I still think we should send the comment links out of band and cache them individually.

That is way better than to force the node to be per user.

This is not only smart-cache, but the node render cache being affected, too.

So like:

#create_placeholder => TRUE,
+#cache => [
+ 'keys' => [...],
+],

instead of removing #create_placeholder.

Edit: Also our auto-placeholdering implementation really missed the case that something does not have cache keys, but a lazy builder and such can be placeholdered, but adding #cache keys would solve it and we could even remove the #create_placeholder because as it is cacheable it would correctly be auto-placeholdered.

Wim Leers’s picture

I still think we should send the comment links out of band and cache them individually.

But that will create many, many, many cache entries!

Fabianx’s picture

#49: Yes, but possibly in combination with your permissions patch way less?

Also could consider a max-age on those ...

catch’s picture

On #44, That only helps if you have no role with that permission. One user accessing a page with that permission is enough to force that cache context for all users, as they are merged together.

It's not quite as bad as this.

All users that have the permission will get the comments rendered per-user.

All users that don't have the permission get the same copy (per permission).

So the performance hit with #44 only applies to roles with that permission. This will help some sites, not those where the authenticated role has edit own comments permission though of course.

Fabianx’s picture

#51:

That only works when we have auto-placeholdering for entries without cache keys, which should be a 3 line patch by now, but which we don't have at this present moment.

Because else if in any variation 'user' bubbles up then the node will vary based on that. (The self-healing part of the Cache Redirect RenderCache system.)

IRC log with more explanation:

11:08 < Fabianx-screen> berdir: WimLeers: I think this is a case where we really need auto-placeholdering to work for something that does not have #cache keys.
11:08 < Fabianx-screen> ( in the case we don't want to micro cache it )
11:09 < Fabianx-screen> Because then:
11:10 < Fabianx-screen> a) if a user does not have the permission => no ['user'] cache context => not placeholdered
11:10 < Fabianx-screen> b) if a user does have the permission, 'user' cache context => auto placeholdered => same as in HEAD right now
11:12 < Fabianx-screen> But auto-placeholdering after rendering concentrated on ensuring it was cached in the same item (on my initiative). We missed that we 
                        should always auto-placeholder after rendering - regardless if we can cache or not. because an upper level would cache it - in this case 
                        the node.
11:12 < Fabianx-screen> And then auto-bubbling would not lead to the problem.
11:13 < Fabianx-screen> as the node varies on the user.permissions.
11:13 < Fabianx-screen> hash
11:14 < Fabianx-screen> and depending on that hash (has permission), it would have placeholders or not have placeholders.
11:14 < Fabianx-screen> which is fine.
11:14 < catch> Fabianx-screen: could we also per-user only if has permission?
11:14 < catch> Fabianx-screen: i.e. everyone else gets per-permission.
11:15 < Fabianx-screen> catch: yes, that is the idea Wim implemented.
11:15 < Fabianx-screen> catch: But we need to ensure that we auto-placeholder, too.
11:15 < catch> Fabianx-screen: doh that was my idea :P
11:15  * catch not awake yet.
11:15 < catch> Fabianx-screen: so the auto-placeholdering is only for those users that have the permission.
11:15 < Fabianx-screen> catch: yes
11:15 < catch> Then they get the same content, and only the placeholder is per-user.
11:15 < Fabianx-screen> catch: yes
11:16 < Fabianx-screen> catch: users with permission get core HEAD performance, users without the permission get better performance.
11:16 < Fabianx-screen> So we can only win.
11:16 < Fabianx-screen> catch: Because if we don't auto-placeholder, then the node would eventually vary by user, too - per our implementation of bubbling.
moshe weitzman’s picture

Sure, a js based solution might take a week to get right. The benchmarking might justify this change during RC.

A time based access control module should be able to take over CommentLazyBuilders service and implement a different buildLinks (or some other hostile takeover).

catch’s picture

I don't think it's reasonable to expect access modules to re-implement CommentLazyBuilder. The whole point of cache contexts was to avoid that, i.e. #2099137: Entity/field access and node grants not taken into account with core cache contexts.

Contextual links already has a js solution that works generically regardless of access implementation, so we know it's possible to do generically (even if we decided not to actually use contextual links here - but that was added to core with the initial contextual links patch and only rolled back due to performance).

moshe weitzman’s picture

I have a fresh idea - don't know if it is valuable or not. The pain here comes from the 'edit own comments' permission surrounding the edit link. We have assumed that this calls for varying by user but thats too pessimistic. Assume that Fred is the author of a given comment. It's edit link only needs two contexts: Fred and NotFred (i.e. everyone else). Thats the set of possibilities when rendering this link (excluding roles). This is not a lot of variations. When rendering 50 comments, we have up to 100 contexts for the edit links. Would it be useful to introduce this sort of uid-boolean context? This is much more constrained than full per-user variation.

Berdir’s picture

Another idea, similar to blocks, cache/handle access separately from actually building the links, and cache a variation for each visible link permutation (e.g. all links, or only edit but not delete). I'm not sure how easy it is, but it might be worth thinking about.

We're also working on #2580551: Optimize getCommentedEntity(), which should make rendering those links faster.

catch’s picture

#55 and #56 both seem worth looking at.

Wim Leers’s picture

Neither #55 nor #56 can be done before RC1. It'll need to be either an RC target or a minor version target. Tagging both; a committer can remove either.

Fabianx’s picture

#55: Yes, the problem is that we need to change cache keys in that case and need to bubble that up - which is impossible.

Dynamic cache keys however are possible when the lazy builder generates those on run-time and goes via a child element + #pre_render / #cache pattern.

e.g. checking all links for access first - but we don't have a language to express that (Fred / NonFred), yet.

That was the idea of #39 to allow Fred / NonFred and cache just the comment links, but you need knowledge of the information here - so it would only work for core.

Likely #52 is the most simple fix, would just need a simple if statement in Renderer. Unfortunately the static cache for placeholdered fragments is in PlaceholderingRenderCache and not in an independent service - like a static cache render strategy.

So this needs some more changes - e.g. making creatingPlaceholderAndCache public or moving somewhere else.

---

Edit:

Turns out user.is:5 would totally work - using a kinda binary map approach to this problem and compatible with reducing to user if needed.

I still think we should placeholder it and cache it independently, e.g. all the comments together.

That would avoid that binary map to bubble up to node cache and dynamic page cache - so less unneeded variation there.

alexpott’s picture

Issue tags: -rc target +rc target triage
moshe weitzman’s picture

I'm holding off working on this issue further in the hope that we get #2580551: Optimize getCommentedEntity(). That would be enough of a speedup that I'd be OK with placeholdering comment links, since the solutions proposed here are non-trivial..

xjm’s picture

Status: Needs review » Needs work
Issue tags: +Needs issue summary update

Thanks for tagging this for rc target triage! To have committers consider it for inclusion in RC, we should add a statement to the issue summary of why we need to make this change during RC, including what happens if we do not make the change and what any disruptions from it are. We can add a section <h3>Why this should be an RC target</h3> to the summary. I gather the TLDR is "performance", but what about the disruption?

xjm’s picture

Version: 8.0.x-dev » 8.2.x-dev
Issue tags: -rc target triage, -minor version target

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Wim Leers’s picture

Issue tags: +Performance

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

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

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

andypost’s picture

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.

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.

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

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.