Problem/Motivation
The original problem/motivation was this:
BigPipe currently only is enabled for requests with a session. The reasons:
- Page Cache (or something equivalent, like Varnish) is much faster, since there's zero calculations happening. So that's much better still.
- Only requests with sessions are personalized anyway. (Either for authenticated users, or for anonymous users with shopping carts for example.)
However, there's one case where BigPipe actually makes sense for requests without a session: in case of a Page Cache miss (or Varnish miss). In that case, we currently make the user wait for the entire page to be rendered, and then we flush it in one go (
\Drupal\Core\Render\Placeholder\SingleFlushStrategy). This means the user is staring at a blank screen much longer than necessary. This is exactly the problem that BigPipe can solve… so what if we use BigPipe to stream the response in case of such a cache miss, and then store the entire result in the Page Cache?As a consequence, a new advanced feature becomes available. If you uninstall Page Cache (and don't have Varnish or something like it), and you configure your site to set
Cache-Control: max-age=0on your responses, you're effectively allowing BigPipe to always stream the response to anonymous users. Which means you could even do things like showing different content on every request, yet still have fast responses thanks to BigPipe's streaming.
Unfortunately, this overlooked a few important facts:
- For anonymous responses, we only know after the response is generated whether it's uncacheable. The
advanced new feature
that the IS said this issue would unlock, i.e. BigPipe automatically streaming all uncacheable responses was overlooking the fact that the "page max age" configuration only applies to responses that do not have customizedCache-Controland meet all request & response policies — but all of that means we can only determine to send a response using BigPipe after the response is already generated, which of course defeats the purpose.
(For authenticated responses, we know for a fact that all of them are uncacheable, so we can apply BigPipe to all of them.) - Drupal 8's PageCache is a reverse proxy like any other. This means no "special access": it relies only what's in the HTTP response, precisely to ensure that anything it does can also be supported in any reverse proxy (Varnish/CDN/…). Until #101, this patch was making the
PageCachelisten to theterminateevent, which is a PHP process concept, not a HTTP concept. So, this constituted "special access".
This alone is not a problem per se: it can be argued that this theterminateevent is equivalent with anend of HTTP stream
event: HTTP responses are streamed, and may take a long time to stream, any reverse proxy could do this.
However, the logic in the PageCacheterminateevent handler was also running PHP code, see next point. - Streamed responses + cache tags: cache tags should use HTTP response trailers. HTTP responses have headers and a body. This is well-known. What is not as well-known, are HTTP response trailers, which can be sent in case of a chunked response:
A trailer allows the sender to include additional fields at the end of a chunked message in order to supply metadata that might be dynamically generated while the message body is sent, such as a message integrity check, digital signature, or post-processing status. The trailer fields are identical to header fields, except they are sent in a chunked trailer instead of the message's header section.(Nobody on this issue knew, but
page_cachemaintainer @znerol did know, see his comment at #2795209-28: Allow streamed responses to be cached by Page Cache.)
BigPipe sends a response in chunks. Some chunks contain placeholders that must be rendered. That rendered content is invalidated by cache tags. So the cache tags for the rendered placeholders should be sent after all HTML chunks are sent.Unfortunately, Symfony does not support response trailers. Varnish supports trailers. Many CDNs do, precisely for this kind of purpose.
- The combination of the two above factors is why we have no choice but to introduce something like
CacheableStreamedResponseInterface, makepage_cacheespecially aware about it, and in the process make it impossible to support this on other reverse proxies. - Conclusion: For responses where cache tags don't matter (uncacheable responses): we can not let BigPipe serve these, because we know too late whether they're uncacheable. For responses where cache tags do matter, we can currently only support
Drupal <> PageCache <> reverse proxybut notDrupal <> reverse proxy. But doing so would make BigPipe very brittle compared to today. So, it's better to put this functionality in a separate module, so that specific sites can choose to adopt this. Over time (once trailer support is present in Symfony for example), we can choose to move this into Drupal core. And it being able to work in contrib means that it can also work as an experimental module. - See #101 for full detail.
Proposed resolution
- Refactor BigPipe internals to allow a contrib module to achieve this. See #104 and later.
- Create a
big_pipe_sessionlesscontrib module (name TBD). See #109 for proof that such a contrib module can elegantly extend thebig_pipemodule in core, including comprehensive test coverage.
Remaining tasks
- Reviews.
User interface changes
None.
API changes
None.
Data model changes
None.
| Comment | File | Size | Author |
|---|---|---|---|
| #123 | big_pipe_no_session_pave_path_for_contrib-2657684-123.patch | 21.73 KB | wim leers |
| #109 | big_pipe-AND-big_pipe_sessionless-2657684-109.patch | 55.65 KB | wim leers |
| #83 | big_pipe_no_session-2657684-83.patch | 64.66 KB | wim leers |
Comments
Comment #2
wim leersDisqus is loaded via JS. No AJAX (or at least not to Drupal, perhaps to an external service, but then we don't need to care about that).
Note that we already designed BigPipe to make this possible. Quoting
\Drupal\big_pipe\Render\Placeholder\BigPipeStrategy:This issue is basically asking to make sessionless BigPipe a configuration option. Assigning to Fabianx for feedback.
Comment #3
cambraca commented@Wim, that sounds about right.
As a side note, I wouldn't mind having some config in
admin/config/development/performance(in a "BigPipe" fieldset, probably). When I first installed the module I wasn't sure it was actually working, I half expected something to be in that page.Comment #4
wim leersLooking up configuration costs valuable time. This is also why the Page Cache is no longer a checkbox on that same performance settings page you mentioned, but a module without configuration. You enable the module, it has zero configuration. BigPipe is similar.
Comment #5
Anonymous (not verified) commentedTo have dynamic content like that you have to use lazy builder to serve logged in users via dynamic_page_cache module. For anonymous users you have two options - either implement page response policy and turn off page_cache on certain pages for certain anonymous users or load the content with javascript. To my knowledge big pipe only allow loading of content ASAP. So I don't think big pipe is where your focus should be.
Comment #6
wim leersComment #7
krlucas commentedBigPipe in core implements a very simple session-based heuristic: session-less content first, session-full content second.
The fact that some session-less content is actually more expensive than some session-full content in a particular use-case is not BigPipe's concern. If we want to implement some sort of hybrid, we can extend (or not) BigPipe and swap the render strategy using contrib.
Comment #8
fabianx commentedRe-opening.
There is 2 cases where session less BigPipe is useful and could be combined with PageCache (that in theory can cache big pipe responses):
Both cases contradict each other (e.g. 1 explicitly wants to cache a BigPipe response, while 2) wants to cache a normal response from the fragments rendered by BigPipe).
1. could be implemented pretty easily by forcing a big_pipe placeholder via #placeholder_options and taking that into account, e.g. the session only mode is only for auto-placeholdered fragments - not for forced ones.
2. could be implemented if rendered placeholders had a 'static' cache somewhere in which case the page cache after the page is delivered would not need to re-render all fragments, but just re-compose the page.
Putting back to active for now - as there are now use-cases, one of them endorsed by Dries. (the spam protection)
Comment #9
wim leersI vehemently disagree with #8.1. It's not at all an effective anti-spam measure. Many spambots actually execute JS. If they don't yet, they can easily do so. Then you're once again at the same place. Dries did not endorse it. He found it interesting. I talked to him about it, and he sees now how he misunderstood it.
However, number 2 is a very interesting possibility, and the strongest reason I've seen so far to make BigPipe sessionless. It's a very compelling feature. And the interesting thing is that after the first response, it will be cached in the PageCache, which means that it effectively would still only be used for responses with sessions, because those requests without sessions will still end up being served by the Page Cache! I love that.
Conversely, if you don't use the Page Cache, every anonymous response can be delivered via BigPipe as well, and when Page Cache is disabled, that's probably because your anonymous responses are highly dynamic too!
This makes perfect sense.
Updating issue title accordingly.
That being said, this could IMO be a new feature for BigPipe for Drupal 8.2. I'd prefer to first stabilize BigPipe. OTOH, this would make BigPipe much more appealing, since it then also improves the experience for sites with only anonymous users. So, +1 for doing this.
I've provided a very, very rough implementation. And it works. The most important remaining problem:
Comment #10
wim leersImplementing my second suggestion at the bottom now.
Comment #12
wim leersAlso, #9 is going to fail many tests. I should have marked it
-do-not-test. Test it manually though, you'll see it works fine.Comment #13
wim leersHere you go. Smaller patch too! :D The interdiff may be confusing because it restores so much, just read the patch itself, it's about the same size anyway.
This makes responses for anonymous users streamed responses, which are delivered much faster. Subsequent responses are just delivered by the Page Cache (are not streamed). In other words, this makes cold cache hits for anonymous users significantly less painful.
Still to do:
Comment #14
fabianx commented#9: Pardon, I had misunderstood Dries reaction there. And yes, they could activate Javascript. However JS parsing costs time and waiting for a fragment to occur costs, too. So deliberate slow down can indeed fight spam.
Anyway: +1000 to #13
That is a great start. Nice idea to use NoJSPlaceholders and make the implementation so simple :).
I think it would be best to add an interface to PageCache so that external services like my Pre-Kernel middleware or BigPipe can interface with it properly - instead of copying the functionality.
Once we have the EmitterInterface, it will be even nicer as we can just do:
And we are done! As BigPipe then no longer is creating and re-creating a new response, but just transforming its contents and removing the emitter!
So that and that refreshless wants to stream a JsonResponse potentially with a custom emitter would be enough for me to say that we should prioritize EmitterInterface, too.
Comment #15
effulgentsia commentedThis seems like a sensible feature to me, but 8.2 is the current branch for new features.
From the issue summary:
This seems out of scope to me for this issue, and AFAICT, not implemented by #13. I see this issue already has the "Needs issue summary update" tag, which I'm guessing should include changing that portion?
Comment #16
wim leers#15: yes, I changed the direction of this issue — I indeed intend to update the IS.
Also note that because BigPipe is experimental, new features can go into 8.1.
Comment #17
wim leersNow making this happen! :)
Comment #18
wim leersFirst, a reroll to allow it to apply cleanly to HEAD again. And one change that was overlooked in #13, that allows it to pass tests. Of course, this new functionality itself still needs tests, but the fact that it's green despite the extensive existing test coverage proves that this slots in cleanly. :)
Also, because this is an experimental module, this new feature can actually go into Drupal 8.1 too. So, moving to 8.1.x-dev.
Comment #20
wim leersUpdated IS, addresses #15.
Comment #21
wim leersThis fixes the most important failures, which were simply a case of me not yet having updated the expectations in
BigPipeStrategyTest. This should bring it down to one failure.More work to follow, of course.
Comment #24
wim leersI wanted to clarify that in fact BigPipe has always supported anonymous users. It just has only supported anonymous users with sessions.
This issue is adding support for the case of anonymous users without sessions.
It's still going to be recommended to use Page Cache/Varnish/… if your responses are not dynamic (i.e. identical for all sessionless anonymous users). However, if your response is dynamic (e.g. customized based on IP address, or changing every request, or changing every second, or …), then BigPipe for sessionless requests can indeed be very appealing, since it brings the ability to stream those responses, which means those responses will render much faster.
And in a default Drupal 8 install, that means we'll be streaming the first response for a sessionless anonymous user. In other words: Page Cache misses will be streamed the first time, and subsequent times they will not be streamed, since Page Cache is much faster, so streaming is pointless (there's nothing expensive to compute anymore).
Was that not yet clear in the IS overhaul I made in #20?
Note that this will mean that https://www.drupal.org/documentation/modules/big_pipe + https://www.drupal.org/documentation/modules/big_pipe/environment will need to be updated. The trigger for streaming should still be the presence of
Surrogate-Control: no-store, content="BigPipe/1.0", but it should not disable caching (theberesp.ttl = 0sline should be removed from the example in the docs). BigPipe already appropriately setsCache-Control: privatefor requests with sessions. It won't set it for sessionless requests (i.e. this issue). This then signals to Page Cache/Varnish/… that it can be safely cached.Comment #25
wim leersAnd here is then comprehensive test coverage.
This made me realize one thing that I did not realize before, not even in #24: it's impossible to support cache tags on this. This follows from:
But we of course need responses streamed by BigPipe in case of a Page Cache miss to be invalidated when appropriate when stored in Page Cache. We can ensure this for Page Cache because it introspects the the cacheability metadata stored in
\Drupal\Core\Cache\CacheableResponseInterface. That works fine. The only problem is that the design of HTTP prevents us from communicating these cache tags to the outside world.(In other words: the data dependencies of a streamed BigPipe response are only discovered during streaming, but the use of headers requires all data dependencies to be known ahead of time.)
So, to use BigPipe for sessionless anonymous responses means partially giving up the ability to use tag-based invalidation on Varnish for sessionless anonymous responses. Partially, because: A) all content outside placeholders is still instantly invalidated, B) content inside placeholders is now going to depend on the max-age of the response. Furthermore, it's very rare to see cache tags be used in placeholders, usually the content is not cacheable at all.
All of this is a consequence of this issue allowing BigPipe to be used for cacheable responses — so far BigPipe only supported uncacheable responses, because responses personalized for a particular session can never be cached.
Comment #27
wim leersIt's at times like these that I miss the old testbot. The output in #25 is highly misleading. There's indeed only one failing test. But the error messages that it shows suggest there's only one real problem. But there's many more:
There's twelve failures, yet https://www.drupal.org/pift-ci-job/378101 only shows the two exceptions.
Comment #28
fabianx commented#25: Lets not overcomplicate the issue.
It is enough to not store the BigPipe'd output in Varnish for now.
For cacheable responses, we most likely want the same behavior as with page cache:
--
For Varnish this changes a little to three requests:
For true Varnish BigPipe thingy, you need BigPipe-ESI, which unfortunately Varnish 4 does not support right now (only the commercial version), but Fastly should support it soon.
Comment #29
wim leersThis patch then brings the first part of the actual solution: removing the need for Page Cache hacks.
First, we need to remember the request/response flow. From
index.php:In the case of a non-streamed response, 99% of the work is done on the first line, which means that the
PageCachemiddleware can store the response in itshandle()method. But in the case of a streamed response, the bulk of the work is actually done in the second line: that is when placeholders are rendered. Which means that thePageCachemiddleware cannot store the response in itshandle(), because the response still contains placeholders! However, Symfony actually provides a solution for this:TerminableInterface. Middlewares can implement this too.So the solution is simple:
This patch:
StreamedResponseInterfaceand letsBigPipeResponseimplement that interface. This interface allows the entire streamed response to be read during the termination phase.PageCacheto not only ignoreBinaryFileResponse+StreamedResponse, but alsoStreamedResponseInterfaceDenyBigPipeResponseresponse policy that was introduced in #13 unnecessary, so deleted thatPageCachemiddleware also implementTerminableInterface, and in there, if the response that was just sent implementsStreamedResponseInterface, then let it cache the streamed response. This means that some of the logic that lives inPageCache::fetch()in HEAD needs to be shared amongPageCache::fetch()andPageCache::terminate()— so that logic has been moved into aprotected PageCache::storeResponse()method.This means we'll need more Page Cache test coverage: we need to test that its
TerminableInterfacebehaves as expected.The test-only interdiff/patch on this comment brings the updated test coverage. The other interdiff/patch brings the actual changes.
Comment #30
wim leersBetween #25 and #29, we now have comprehensive test coverage. Updating the IS.
Comment #31
fabianx commented#29: The StreamedResponseInterface is a pretty neat idea.
I wonder if we should not combine it with the 'delivery method' I proposed with Crell and just use setContent() / setCacheabilityMetadata() on the response itself?
Also I feel you want to execute all of that in its own rendering context or do the sub-response fragments take care of that already?
Great work!
Comment #34
wim leers#31:
Glad you like it :) It's the simplest solution possible AFAICT.
Can you elaborate on this? I don't know what you are referring to.
First, this remark is out of scope for the current issue.
Second, BigPipe already does that in HEAD, and always has.
\Drupal\big_pipe\Render\BigPipecalls\Drupal\Core\Render\Renderer::renderPlaceholder(), which renders in its own render context.Yay! :)
You'll like the next round, which will make this patch green. Stay tuned.
Comment #35
wim leersIn the mean time, this removes all remaining Page Cache-related hackery from the BigPipe module.
Comment #36
wim leersThis patch then brings the second part of the actual solution: making BigPipe implement
StreamedResponseInterfaceso that Page Cache can cache it.This patch:
BigPipeResponseto make it implementStreamedResponseInterfaceBigPipeResponse,BigPipeResponseis no longer a super dumb class, consequently it makes sense to move some of the BigPipe header logic fromHtmlResponseBigPipeSubscriber.phptoBigPipeResponse— even more so because two of the headers (Cache-ControlandSurrogate-Control) now depend on whether the BigPipe response is for a request with or without a sessionBigPipeResponsemust already be aware of it being for a request with/without a session, it makes sense to let this knowledge be passed on to theBigPipeservice that it calls for actually streaming the response — soBigPipe(Interface)::sendContent()gained a new parameterHtmlResponseobjects rather thanstrings to track the streamed output.Comment #38
wim leersStrange, #36 was not picked up by testbot. Reuploading.
Comment #40
wim leersHah, now there's failures in
BigPipeResponseAttachmentsProcessorTest. I didn't run that unit test locally. Will fix those fails.Note that all other tests pass, so that includes the BigPipe and Page Cache integration tests!
Comment #41
fabianx commentedI wanted to discuss that in IRC, but since found my arguments, so writing here:
- As this now changes BigPipeResponse a lot, I would like to implement #2577631: Allow HtmlResponse to use a flexible emitter first, likely the response would still need to be passed from the emitter to the BigPipe service, but I was a fan of that from the start anyway.
The reason being that the emitter or BigPipe service can just call setContent(), setAttachments() and setCacheabilityMetadata() and unset the emitter itself to update the response to the final emitted product and it will just work.
We would not need two new Interfaces, but we could still have them.
Creating a new response and embedding and returning feels strange from an architecture POV.
And Crell was also much in favor of the EmitterInterface and its in Zend Diactoros for PSR-7 (?) https://api.drupal.org/api/drupal/vendor!zendframework!zend-diactoros!sr....
So this feels like the right moment to introduce it.
Comment #42
wim leersAdding
ResponseEmitterInterfaceandEmittableResponseInteracewould be nice, but … we'd still need a way for retrieving the emitted response.In other words, you're asking for:
ResponseEmitterInterface(with only aemit()method), and letBigPipe(Interface)implement itEmittableResponseInterface(with a getter+setter for getting/setting aResponseEmitterInterface)StreamedResponseInterfacehas (introduced in this patch) toEmittableResponseInterfaceThen implementation-wise, that'd mean:
zend-diactoros, but sadly this is impossible because it requires the use ofResponseInterface, which Symfony does not implementzend-diactoros, so we'd roll our ownEmittableResponseInterfacethat is unnecessaryPoint 3 clearly indicates that this is actually completely independent. It's only tangentially related — we'd still need
StreamedResponseInterface, becauseEmittableResponseInterfacedoes not have a way for retrieving the emitted/streamed response.So you're exactly right at:
We could merge them, but then we'd be merging different concerns. Not every streamed/emitted response can (think real-time log data) or should (think streaming of binary files) be retrievable after having been streamed/emitted. So the interfaces are logically different, and must therefore be separate.
No they can't. Look at
BigPipeResponse::getStreamedResponse(). It returns aHtmlResponseobject, precisely because it's returning a non-streamed ("fully flushed" if you will) variant of the same response.I'm strongly doubtful whether this is the right time to introduce response emitters. We need >=2 use cases to prove it's the right abstraction. Drupal 8 has only a single implementation (BigPipe). Yes, it's inspired by Zend's diactoros, but we're adding some of our own stuff on top. Which makes it fairly risky to add this new official API.
I'd much rather do only the one thing that we'll still need to do anyway: add
StreamedResponseInterface. We can still do #2577631: Allow HtmlResponse to use a flexible emitter later.Comment #43
wim leersAnd this then provides the minor changes to
BigPipeResponseAttachmentsProcessorTestto allow that test to construct value objects necessary in its testing.Comment #44
fabianx commenteda) StreamedResponseInterface is misleading => Should be CacheableStreamedResponseInterface as the important point is that its cacheable after streaming, not that its a streamed response.
Just some train of thought on emitter vs. non-emitter:
So the control flow currently is:
Partial HtmlResponse => BigPipeResponse + embedded original partial response
The PageCache then checks for an embedded response.
My suggested control flow would be:
- HtmlResponse + some flag that it is uncacheable (e.g. $this->hasEmitter() or $this->isPublic() == FALSE).
- Then HtmlResponse() just gets made cacheable by updating the content
However there is one drawback to the emitter approach:
A Middleware could in theory work with a HtmlResponse using getContent(), but that just returns partial content, while it could detect a BigPipe or CacheableStreamedResponseInterface.
But on the other hand a HTML response can in itself include placeholders, too.
And further getContent() works on the BigPipeResponse as well, so that is not a drawback per se as its similar to check if a response has placeholder attachments (another middleware could take care of that) or check for BigPipeResponse or check for an emitter that this response is using.
--
And we might even think about using "noJS BigPipe by default in Drupal" in the future as there is no drawback.
The only incompatibility comes when a middleware calls getContent() and setContent() and that we could change by ensuring that when getContent() is called the emitter is called with ob_level() > 0. As the emitter also updates the original response, that would mean that the response is then fully build (and was not streamed obviously).
--
All that said, I think using EmitterInterface is cleaner:
- Less copying and cloning around (though we already do that)
- Emitters update the original response as well and make it cacheable again
- PageCache just checks in terminate() again if its cacheable now (I think we might use the private() flag for that). And in terminate() if we do an instanceof check or an instanceof check plus one function call is not that important performance wise.
- We could also instead just always cache in terminate(), which would also very very slightly improve performance as the users get their response faster. (not of any real significance though).
- Then we just need to check once if its cacheable.
The reason why its important to discuss that now is that we introduce here a new interface, but an emitted response just needs to be cacheable after emitting (and reasonably if someone unsets the emitter that would be a good reason to think that), not embed another response in itself.
e.g. there is an inherent conflict in that both approaches have different needs, but we can't break BC after introducing it.
Hence I think its important to get it "right" the first time.
And I do think much more things will use streaming in the future, which emitters allow easily.
Comment #45
wim leersInteresting! This is a superb remark. We should modify
StreamedResponseInterface::getStreamedResponse()to not havebut
So +100.
I'd go with
instanceof INTERFACENAME. (See #2577631-33: Allow HtmlResponse to use a flexible emitter, I proposed to not call thatEmitterAwareResponseInterface, butStreamedResponseInterface.) Having another method would require it to live on the interface, but it'll just always have to return TRUE then. It's the same thing twice. Either way, you have to check for it: either by calling a method, or checking if an interface is implemented. The latter is DRY, so go with that.No! A private response could also be cacheable. Page Cache does this.
I agree that this is not really a drawback. The same caveat/need for carefulness already exists, and is inherent to Symfony's
Responsedesign.See how you say "emitters for streaming", this is why I proposed at #2577631-33: Allow HtmlResponse to use a flexible emitter to just call them streamers.
I'm cautiously optimistic that doing #2577631: Allow HtmlResponse to use a flexible emitter actually is the right thing to do.
Comment #46
fabianx commentedWe discussed this in IRC and the summary is that even with emitters we want CacheableStreamedResponseInterface.
The reason is that we want to run kernel response event listeners again when we have fully assembled the response.
This however we can only do if we stored the original response and cloned it, as response listeners might have changed it before.
Therefore we keep the original response and we need an interface for how to retrieve the original response as Wim is right and those are semantically two different responses.
That means we would have:
- setCacheableStreamedResponse()
- getCacheableStreamedResponse()
and getCacheableStreamedResponse() can also return NULL.
--
What was missing before is that the response again goes via the Kernel Response Event Listeners, but as attachments are empty, only those listeners related to cacheability will run.
--
I would love if HtmlResponse itself would have a copyResponse / duplicate / duplicateFrom / duplicateResponse method.
BigPipeResponse could then just call:
to init itself instead of cloning from the outside. That would make me more comfortable, but could be a follow-up, too.
Comment #47
wim leersFirst, a straight rebase. #2678662: Ensure BigPipe does not break when HTML document contains CDATA sections or inline scripts matching certain patterns landed recently, and conflicted with this.
Comment #48
wim leers#47 was a bad rebase. Copy/pasted a statement to the wrong place.
Comment #50
wim leersFirst, renaming
StreamedResponseInterfacetoCacheableStreamedResponseInterface, updating its@returndocblock, marking it@internalwith@todoto make it a public API in the future.Next: the response event subscriber stuff.
Comment #52
wim leersOne rename was incomplete.
Comment #53
wim leersCR created: https://www.drupal.org/node/2779719.
Comment #54
wim leersComment #55
wim leersThe reason Fabianx and I agreed that this patch should be changed to dispatch the
KernelEvents::RESPONSEevent is consistency/predictability: this cacheable streamed response should be processed in the exact same way as any other response. And so, also in this case, we need for exampleFinishResponseSubscriberto run so that theX-Drupal-Cache-Tagsheader is set correctly, and that means it must include cache tags added by a rendered lazy builder that is streamed to the client. Which means that we must invoke the RESPONSE event again for the cacheable streamed response.In fact, the new test coverage that this patch is already adding proves this flaw:
This verifies that a repeat request is a Page Cache hit, that is not handled by BigPipe. But that last line is asserting that the cache tag set in a
#lazy_builderrendered by BigPipe and whose output is streamed, does NOT exist on the cached response. This is wrong! It should exist! (Otherwise external reverse proxies wouldn't be able to invalidate their cached responses based on this cache tag being invalidated, as explained earlier in this comment.)So, simply changing that to
provides the necessary test coverage.
Comment #56
wim leersAFAICT I implemented everything that Fabianx and I discussed in #46. (With the exception of that
duplicate()method, which Fabian already mentioned can easily be a follow-up, and I'd argue it must be, because it's strictly out of scope for this issue.)With that, I'm signing off for the next 3 weeks — going on our honeymoon to Japan! :D
Comment #58
wim leersI'm still awaiting reviews. As far as I'm concerned, this is ready.
While awaiting that, here's additional test coverage to prove that this does not break the
\Drupal\Core\EventSubscriber\ActiveLinkResponseFilter.EDIT: why this extra test coverage? Well, upon re-reviewing this, I thought that was the only thing that could break. This test coverage proves it doesn't break.
Comment #59
fabianx commentedI finally was able to finish reviewing this.
It is RTBC.
Comment #61
wim leersRandom fail in
Migrate_drupal_6.Drupal\Tests\taxonomy\Kernel\Migrate\d6\MigrateTermNodeTest. Retesting.Comment #63
wim leersHm, happened again. Same random fail. I verified that D8.2 HEAD still passes tests, so it's not that HEAD is broken. I suspect a bad testbot? Retesting again. Let's see.
Comment #65
wim leersOnce again that same random fail. Note that it includes
PHPUnit_Framework_Exception: zend_mm_heap corrupted. I can't reproduce locally. Help :(Comment #66
fabianx commented#65: zend_mm_heap corrupted is usually an opcache issue, where it runs out of memory or tries to free the memory twice.
It is usually a PHP 5.6 only bug usually and very hard to track down. (though in this case it is PHP 5.5)
Several sites I work with have experienced this in one fashion or another and usually increasing the memory size for opcache helps. And sometimes restarting php-fpm after deployments is the only help.
I am gonna ask Mixologic if we throw away the container for the apache or if we re-use it and just clean it. Though even then I am pretty sure we restart apache before the test.
Comment #67
wim leersGreen on PHP 7! Failed on both 5.5 and 5.6. Retesting those, now that additional commits have been made to 8.2.x.
Comment #68
wim leersAnd now it's green on 5.5, but red on 5.6. Gaahhh!
Comment #69
fabianx commentedLets go back to RTBC and see what happens.
Comment #70
effulgentsia commentedI queued a PHP 5.6 test for 8.3.
Comment #71
effulgentsia commentedLooks like the same failure on PHP 5.6 on 8.2 and 8.3. Looks to me like it's maybe also the same failure as in #2762549: Drupal\field\Tests\Update\FieldUpdateTest, Drupal\views\Tests\Update\EntityViewsDataUpdateTest and Drupal\comment\Tests\CommentFieldsTest fail on 8.1.x. I'm not clear if HEAD is failing the same way or not, of if there's something in this patch that's similar to what's triggering it in that one.
Comment #72
xjmThis can't be RTBC with a random fail being introduced, unfortunately. The only reason that the fail for
Drupal\comment\Tests\CommentFieldsTestwas not reverted is that it was introduced by a fix for a data loss critical. This is an experimental module.However, I don't think this is actually the same fail as #2762549: Drupal\field\Tests\Update\FieldUpdateTest, Drupal\views\Tests\Update\EntityViewsDataUpdateTest and Drupal\comment\Tests\CommentFieldsTest fail on 8.1.x? Are there
usort()happening in this patch that I missed?Comment #73
xjmOh, I do see https://www.drupal.org/pift-ci-job/443495 exactly above -- not the same bug elsewhere, but just the existing known fail. Is there a different one? Was there a different fail on 5.5? I don't see the other test results mentioned now. :( We should always document the exact fails, not only whether it is green or red.
In general, if a test failure does not have an existing critical for it, we should file one if it occurs in HEAD (and always check that list first). A test issue can be used to test whether it occurs in HEAD if needed.
Comment #74
xjmWhat we could do is actually comment out CommentFieldTest in a testing patch based on this issue's patch, and queue multiple 5.6 runs for that test patch, to make sure the other 5.6 tests are all passing outside of the known bug in HEAD. However, that doesn't address the other fails that occurred before.
You can always look through http://drupal.org/node/3060/qa to see the past and present fails on HEAD.
Comment #75
wim leers#73: #61, #65 and #66 all talk about the same exact fail.
But it doesn't occur in HEAD. There's no sane/simple way to reproduce this, because it's something deep in the PHP engine. I have no idea at all how to reproduce this in a separate issue, other than posting this same patch. This fail was not occurring when Fabian RTBC'd this in #59.
Comment #76
xjm@Wim Leers and it was not CommentFieldTest? CommentFieldTest fails in HEAD on 5.6 100% of the time. If that was the only thing that ever failed, we can mark this RTBC. If it is some other test though, or against some environment other than 5.6, that's when we should be concerned.
Comment #77
wim leersNo, as I said, it was a migration test (specific one mentioned above). But that was with PHP 5.5. The fails are now isolated to 5.6, and are indeed for CommentFieldTest. But I'm pretty sure that the 5.6 fails *initially* were also in that same migration test.
(Also, shows clearly where DrupalCI falls short. We shouldn't have to manually write down exactly what failed.)
Do you want me to upload the same patch against 5.5 10 times?
Comment #78
wim leersI just read #2762549-54: Drupal\field\Tests\Update\FieldUpdateTest, Drupal\views\Tests\Update\EntityViewsDataUpdateTest and Drupal\comment\Tests\CommentFieldsTest fail on 8.1.x, -55 and -56.
The thing is that PHP is apparently known to have memory corruption bugs that are only reproducible by certain combinations of code. Quoting DrupalCI maintainer Mixologic:
Let me stress the most relevant bits:
Those fails disappeared now (since #68). SO YAY! That's "fixed". There's nothing special we need or can do for fixing those random fails that had memory corruption.
But now we have a different fail. And apparently (per #2762549: Drupal\field\Tests\Update\FieldUpdateTest, Drupal\views\Tests\Update\EntityViewsDataUpdateTest and Drupal\comment\Tests\CommentFieldsTest fail on 8.1.x-54]) it is known that that fails. Apparently that always fails, even on HEAD: https://www.drupal.org/node/3060/qa. I first didn't notice that that was a different fail.
Conclusion: all of xjm's concerns are addressed. This doesn't introduce new random fails. The random fails we had before were due to PHP bugs, that the DrupalCI team has seen many times before. The current fails I first assumed were the same, but they are apparently not; they're known fails. Because I believed that, the last few comments I made (#75 and #77) were basically just a distraction. Apologies!
Comment #79
xjmThanks @Wim Leers! Maybe we can get this in before RC after all, yay.
I queued 10 runs against 5.5 with the latest patch to make sure all is indeed well.
Comment #80
wim leersThey all came back green :) Happy to see my words confirmed.
Comment #81
effulgentsia commentedThis patch makes changes to PageCache.php, so I'm not comfortable with it going into 8.1 at this point. I think 8.2 is fine, especially if we do it prior to 8.2.0. So, changing the issue's version accordingly.
Given that 8.2 RC is almost upon us, I wanted to first review the changes made to code outside of the Big Pipe experimental module. Here it is:
Do we need this trait to be in
Drupal/Core/Renderduring 8.2? I think my preference would be to put it into the big_pipe namespace for 8.2, then have an 8.3 only issue to move it intoDrupal/Core/Render. My reason for that will make more sense at the end of this comment, but I'm open to arguments for why 8.2 contrib would benefit enough from this trait to make it worth rushing in.In addition to what this method is doing in this patch, it MUST also delegate, per https://github.com/stackphp/builder/issues/14.
The last sentence is confusing. What does "already" mean in this context? Where else can that header be set, since the terminate event can't set headers, given that it fires after the response is already sent? And given that, I think the sentence can be removed entirely.
This should move into the
storeResponse()method, since policies should also be checked forCacheableStreamedResponseInterfaceresponses.Meanwhile, this should move out of the storeResponse() method and into the fetch() method, since per point #2 above, we shouldn't be messing with headers during the terminate() stage.
Since I haven't yet sufficiently reviewed the changes to Big Pipe itself, I'd like to request that a child issue be made with all of the changes outside of Big Pipe, with the above feedback addressed. That I would feel comfortable committing into 8.2 prior to RC (especially if the trait isn't included per point #1). Then, given that Big Pipe is still experimental, I think its changes could be committed during the RC phase, or even into 8.2.1, unless a release manager disagrees with that.
Comment #82
wim leers+1 for 8.2-only.
Had a call with @effulgentsia about #81, and how he'd like to see it addressed.
PageCache, we felt it was best to have a follow-up to add unit tests for all Stack middlewares: #2795199: Unit tests for all Stack middlewares.Furthermore:
PageCache, by being more careful with moving of code.@returnsdocblock and incorrectly orderedusestatements.In this reroll, only adding the test coverage to address #81. In the next reroll, you'll find the fix.
Comment #83
wim leersAnd here's the fix. (Note that the moving of code to minimize changes to
PageCacheactually make this interdiff look more complex than it is.)Comment #84
wim leersDone: #2795209: Allow streamed responses to be cached by Page Cache.
Comment #86
fabianx commentedTo the delegation of Middlewares:
Would that not break our code that already traverses all terminable middlewares one by one and calls terminate() on them?I feel that is a needed follow-up discussion, but has no place in this issue?Edit:
Discussed in IRC:
The change in https://github.com/stackphp/builder/commit/17887ecd4112b04d8d9c5327bc05b... StackPHP was technically a BC break, as it broken one assumption (its own) and replaced with what projects did.
We had added Stack before that commit, which is why I still had the unconditional foreach in mind.
IMHO the change makes no sense at all (as the terminable part is not decorated anyway due to it being a conditional interface), but as it was done, we have no other chance, than to comply ...
Comment #87
fabianx commented.
Comment #88
fabianx commentedBack to RTBC.
Comment #89
xjmGood catch @effulgentsia. I did not realize this patch included changes outside of BigPipe; that is unfortunate. Changes like adding interfaces and functionality to stable core should have only been targeted against the minor version, which would have meant 8.3.x as of beta1 a month ago. In Outside In, we asked that such changes be moved to separate issues scoped to the problem they were trying to solve, and that they be done before beta. In general issues for an experimental module should not include any changes to stable code at all.
I guess that is what #2795209: Allow streamed responses to be cached by Page Cache is? But that issue is not beta-eligible, much less rc-eligible.
Comment #90
xjmAlso, this issue is not RTBC then.
Comment #91
xjmActually postponed on the blocker, I guess.
Comment #92
wim leers:( :( :(
It's contained that code since #13 (almost 3 months ago), i.e. since the first version of this patch. Since the changes to
page_cachemake zero sense without the rest of this patch, I didn't think of moving it into a separate issue. It's one logical/semantical unit/commit.So, alas, this will be a big BigPipe feature for 8.3 then.
But I agree with core committers that regardless of risk, committing changes to
page_cachewould be violating the rules and semver principles that allow us to have those nice frequent releases.Comment #93
effulgentsia commentedI opened #2795391: Move most of PageCache::fetch() into a storeResponse() helper that can be independently invoked from a subclass and hope that that is still reasonable to get into an 8.2 patch release, if not during RC. If that's allowed, then I think this issue can still be made to work for 8.2, by having the Big Pipe module swap out the
http_middleware.page_cacheservice with a subclass ofPageCache. Therefore, moving this back to 8.2.x for now.Comment #94
effulgentsia commentedI discussed #93 with @catch and he'd prefer BigPipe to land into 8.3 cleanly first (meaning not needing to subclass PageCache). While he's theoretically open to an 8.2 backport after that, he'd also be fine without such a backport. Therefore, moving this back to 8.3.
Comment #95
mustanggb commentedI was testing RefreshLess recently and noted that it wasn't working for anonymous users, after finding this issue I realise this was probably just sessionless users. So I was just wondering if this issue would help with that situation.
1. Are these changes "compatible" with the idea of allowing RefreshLess to work with anonymous/sessionless users?
2. If yes, will this patch just magically allow it to work?
3. Or will it just provide the groundwork?
4. Alternatively are these completely unrelated issues and I'm looking for answers in the wrong place?
Comment #96
fabianx commented#95 It might be related, but its hard to say. Best would be to open a support request for refreshless with your use case.
Comment #97
fabianx commentedPostponed now on #2795391: Move most of PageCache::fetch() into a storeResponse() helper that can be independently invoked from a subclass
Comment #98
andypostWhy this still postponed?
Comment #99
fabianx commentedNo longer postponed - indeed.
Comment #100
wim leersI've resumed working on this earlier today. Expect an update soon.
Comment #101
wim leersSo, I started out with rerolling this patch. Then read #2795391: Move most of PageCache::fetch() into a storeResponse() helper that can be independently invoked from a subclass and looked at how that impacted this patch. Which led me to read @znerol's comment at #2795209-28: Allow streamed responses to be cached by Page Cache. In there, he formulates his concerns/criticism on the impact of modifying
PageCacheto become aware of the new\Drupal\Core\Render\CacheableStreamedResponseInterfacethat this issue introduces. Let me quote it in full here:@znerol at #2795209-28:
I just posted a response to that at #2795209-31: Allow streamed responses to be cached by Page Cache, also quoting verbatim:
In other words: thank you @znerol for raising those concerns! You've convinced me that the current state/direction of this patch are a bad idea.
So, of the 4 options I identified for proceeding:
25 files changed, 764 insertions(+), 81 deletions(-)). It would also mean we can close this issue, and can move immediately to #2797169: Mark BigPipe as stable/non-experimental.With that, I'm assigning this to @Fabianx and @catch for feedback.
Comment #102
effulgentsia commentedI'd need to get up to speed with this patch again and with the comments referenced in #101 to post a more thorough comment, but for now...
I think
big_pipe_sessionlessbeing either in contrib or in a core alpha-stability submodule is fine. But so long as it requires significant overriding of what's inbig_pipemodule, I don't think we should promote big_pipe to stable. I'd rather us figure out what we need to do to core big_pipe module in order to make a big_pipe_sessionless module possible without "significant" overrides. "Significant" is of course a judgment call and a sliding scale.Yeah, this definitely feels weird. Long term, it would be great if
big_pipe_sessionlesscould figure out a way to not have a hard dependency on page_cache, but especially while it's an experimental module, I think it's fine for it to have that dependency.If an architecture that completely decouples from middleware is too hard, could we at least make
big_pipe_sessionlessextend/decorateDrupal\page_cache\StackMiddleware\PageCacherather than needing to apply the questionable changes directly topage_cachemodule?Comment #103
wim leersI disagree with this rationale. To make advanced contrib modules that override/extend parts of core easier to implement, we refactor internals of affected core modules as necessary. I don't think it needs to be different here.
But that's physically impossible until Symfony and Page Cache gain trailer support. Once they both gain trailer support, that changes things. Then sessionless BigPipe can work with reverse proxies that support trailers, without needing
page_cache.In other words: in that case,
page_cacheis just like any other reverse proxy again: any reverse proxy that supports reading the cache tags trailer (header) will work fine.Yes, of course. That's what I said in my response to #28.4.A):
While awaiting the response of @effulgentsia, @Fabianx and @catch, I'll already get started on a
big_pipe_sessionlessmodule, so I can properly observe where pain points are, and where we can refactor BigPipe internals to make such a module more feasible.Comment #104
wim leersThis addresses #102. The attached patch includes all changes I'd propose to make as part of this issue. They make it possible for contrib modules like "BigPipe Sessionless" to work. All code changes in fact improve legibility of the existing code!
This interdiff (
interdiff-everything.txt):core/modules/page_cachechangescore/modules/big_pipemodules/big_pipe_sessionlessThe end result is:
big_pipe_no_session_pave_path_for_contrib-2657684-104.patch, which contains all of the above, minus thebig_pipe_sessionlessmodule, seeinterdiff.txtfor just this portionbig_pipe_sessionless.module-2657684-104-do-not-test.patch, which contains the new module, 99% finished, and fully verified to be operationalBack to you, @effulgentsia. I think/hope this convinces you.
Comment #105
effulgentsia commentedAwesome! This is just a review of the
big_pipechanges, notbig_pipe_sessionless.This is exactly the kind of API change I'd much rather make before marking big_pipe stable than after. However, big_pipe is already documented as beta stability, so I think this might be worth doing in its own issue for greater visibility both during discussion and in the commit log.
This seems like an API change to me as well, so same point as above for sendContent(). https://www.drupal.org/core/d8-bc-policy excludes constructors from the API only for service objects, plugins, and controllers, which this isn't one of.
I'm not clear on whether this is an API change, a behavior change, or just a no-op clean-up. What's the reason for it?
Looks like this is being added, rather than moved from elsewhere. If HEAD is working without this being done, what about this patch makes this now necessary?
Comment #106
wim leersRegarding API changes in BigPipe in general: I created #2835604: BigPipe provides functionality, not an API: mark all classes & interfaces @internal.
Cache-Control: privateby default, which is why the test passed. Still, we should add back that line just to\Drupal\Core\Render\HtmlResponseAttachmentsProcessorand\Drupal\Core\Ajax\AjaxResponseAttachmentsProcessor. I'd be happy to move this to a separate issue though, it's minor clean-up.Comment #107
wim leersSo, if we look at this patch, we can summarize the code changes as follows:
HtmlResponseBigPipeSubscriber: simplify, by moving logic intoBigPipeResponse. The moved logic 100% applies toBigPipeResponse. This therefore makesBigPipeResponsemore self-contained.BigPipe: rather thanprint $chunk; flush();, call the newsendChunk($chunk)helper method. Inbig_pipe_sessionless, we can then subclass this and also track all sent chunks. Also introduceperform(Pre|Post)SendTasks(), to perform tasks before and after streaming (sending) content. In BigPipe, we use this to start/stop the session (for placeholders whose content depends on the session). Inbig_pipe_sessionless, they are overridden to do nothing.BigPipeInterface: changesendContent(string $content, array $attachments)tosendContent(BigPipeResponse $response). This allowsbig_pipe_sessionlessto access other data. Plus, it just makes sense: pass theBigPipeservice (which is really the heart of the BigPipe module) theBigPipeResponsevalue object, rather than just seemingly arbitrary subsets of it.BigPipeResponse: change the constructor to receive the original HTML response (necessary to allow the logic to be moved into this class, out ofHtmlResponseBigPipeSubscriber), plus the ability to access the original response (necessary forbig_pipe_sessionless, so that it can construct a correct "non-streamed" HTML response of the streamed content for caching in Page Cache), plus the minor clean-up mentioned in #106.4.BigPipeStrategy: move a small bit of logic into a helper function, so thatbig_pipe_sessionlessdoes not have to duplicate it.All of this is in fact making the BigPipe module cleaner!
Finally, looking at the test changes, we:
BigPipeTest::getTestCases()andBigPipeTestController::test()should arguably be removed, but they'd require needless duplication inbig_pipe_sessionlessHopefully this simplifies the review process.
Comment #108
wim leersThis removes the unnecessary changes in
BigPipeTest::getTestCases()that I mentioned in #107.tests.1.Comment #109
wim leersI now finished the
big_pipe_sessionlessmodule, by moving #108 over to8.3.x, wherePageCache::storeResponse()is available.In
interdiff-everything.txt, you can see:BigPipe: extracted a new protected helper methodBigPipeTestController::test()that I mentioned in #107.tests.2big_pipe_sessionlessnow depends on Drupal 8.3.0big_pipe_sessionlessnow alters the Page Cache middleware class, to expose a public function that allows it to store a responsebig_pipe_sessionlessIn
interdiff.txt, you can see all changes that affect core'sbig_pipemodule:big_pipe_no_session_pave_path_for_contrib-2657684-109.patchis still the patch to review.interdiff.txtis still the interdiff to review.big_pipe-AND-big_pipe_sessionless-2657684-109.patchandinterdiff.txtare posted only to prove that it's possible to implement this in contrib.For core, this patch now means
8 files changed, 204 insertions(+), 76 deletions(-)instead of8 files changed, 209 insertions(+), 74 deletions(-)(#108) or even25 files changed, 764 insertions(+), 81 deletions(-)in the previous direction (#83).Comment #110
mustanggb commentedComment #112
wim leersThe patch in #109 that included
big_pipe_sessionlessfailed inDrupal\system\Tests\Module\InstallUninstallTest. Just because that test is also installing contrib modules. Its failure is irrelevant, because what matters is that its test coverage passed, which it did:So, #109 proved that this module can work in contrib.
Comment #113
wim leersIn this new scope+direction:
Do we have consensus that this functionality does not belong in core, because of the rationale I gave in #101?
Comment #114
wim leers#2835604: BigPipe provides functionality, not an API: mark all classes & interfaces @internal was committed. This needed a reroll, there was a small conflict in one docblock.
Comment #115
catchJust quickly:
I think it makes sense to put the no-session stuff in contrib.
Using trailers would be fantastic, really like that idea if we can sort out support.
Patch itself looks like good cleanup but haven't done a proper review yet.
Comment #116
wim leersThanks, @catch, that's very encouraging!
Assigning to @Fabianx for feedback too.
Comment #117
fabianx commentedi reviewed both the big_pipe_sessionless and the patch (in the whole history) and it all looks perfect.
All the helper functions make the code more readable, easier to understand and manageable.
My main concern that Varnish could be caching the big pipe response is also unfounded as only the HtmlResponse is marked cacheable - due to how its encapsulated.
A little bit of a pity that we can't ship with this by default due to support for reverse-proxies / CDN and not being able to rely on reverse_proxy setting, but the good enws is that as a user that does not use Varnish you can just use the contrib module then and its also really nice that with that you can just choose whatever big_pipe you want.
This also paves the way for further authcache_big_pipe implementations - where the same trick can be used and it would just need to override the subscriber, yet again ;).
So everything looks fantastic and as I found nothing in my code review either:
This is even RTBC!
Really really great work!
Comment #119
wim leersRandom testbot fail? When I came to this issue, #114 was green again.
Comment #120
alexpottJust a few nits... this looks good.
That comment needs updating.
Missing text description.
Needs wrapping.
These ifs are actually unnecessary.
Is this a string - I guess so. Missing the type.
Comment #121
wim leersisset($attachments['big_pipe_placeholders']and you'll see more occurrences. The reason: some requests/responses use only no-JS placeholders, others use only JS placeholders.assert()for it to guarantee that. :)100% of the changes are comments only, so back to RTBC, and must still be green.
Comment #122
alexpottRe #120.4 https://3v4l.org/PVbMt unset doesn't care if the thing exists on not. Imo it just makes for more readable code ... less to read / care about.
Comment #123
wim leersAha! I didn't know that PHP edge case. I think the current code is clearer, but I don't feel strongly about it. Done.
Comment #126
wim leersComment #128
wim leersComment #129
wim leersDeleted the CR, since it's no longer relevant: this is now solely refactoring of internals, which does not need or merit a change record.
Comment #130
alexpottCommitted 69c2347 and pushed to 8.3.x. Thanks!
Comment #132
wim leersWoot! This unblocks the trivial #2835758: Remove BigPipeInterface and move all of its docs to the implementation, which is the last blocker for #2797169: Mark BigPipe as stable/non-experimental :) :) :)
Comment #133
wim leersMoving it to a separate module made it possible to clearly express the dependency on the Page Cache module for this functionality. So it should not be called "BigPipe Sessionless", but:
I want to move the code in #109 to a contrib module, but of course we need to settle on a name first. Feedback is much appreciated, especially from BigPipe co-maintainer Fabianx!
Comment #134
effulgentsia commentedIMO, that
big_pipe_sessionlessdepends on page_cache and works by priming it directly (breaking the encapsulation of page_cache writes happening solely in middleware) is all implementation details that should be allowed to change without requiring a change to the module name. E.g., if HTTP response trailers become better supported, then those details might change in a way that "BigPipe Page Cache Primer" would no longer be an accurate name.So, I think
big_pipe_sessionlessis an ok name, since it reflects the purpose of the module (to provide BigPipe capability to sessionless requests) regardless of its implementation. Maybe the human-friendly name in the .info.yml should be "Sessionless BigPipe"? There might be ideas for better names, but none are coming to my mind at the moment.Comment #135
effulgentsia commentedMy only question to #134 though is, would
big_pipe_sessionlessever evolve into handling requests with sessions? E.g., in configurations where a reverse proxy can cache responses for requests with sessions? My hunch is no, and that even with such reverse proxies, we would still havebig_pipehandling requests with sessions, andbig_pipe_sessionlesshandling requests without sessions, but I'm not 100% confident on whether that's correct.Comment #137
wim leersThe code in #109 is now published in a contrib module at https://www.drupal.org/project/big_pipe_sessionless. I created a commit history to match the comment history here.
8.x-1.0 release imminent :)