New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support BigPipe streaming in Drupal VM - Varnish VCL and docs update #527

Closed
geerlingguy opened this Issue Mar 23, 2016 · 13 comments

Comments

1 participant
@geerlingguy
Owner

geerlingguy commented Mar 23, 2016

See:

Follow-up issues:

  • #532 - Clean up BigPipe documentation, investigate mod_proxy_fcgi further

For Docs, the preferred process to test BigPipe on Drupal VM is currently:

  1. Set drupalvm_webserver: nginx in config.yml. (Apache 2.4's mod_proxy_fcgi (used by default for Drupal VM) can't support streaming/no buffering).
  2. Add php_output_buffering: "Off" in config.yml.
  3. Add nginx_ppa_use: true in config.yml (I might make this the default for Drupal VM). (see #531).

AFAICT, you don't need to explicitly set gzip off or fastcgi_buffering off as long as you set the X-Accel-Buffering: no header—which BigPipe does by default.

...or for Apache with mod_php5:

  1. Set drupalvm_webserver: apache in config.yml.
  2. Add php_output_buffering: "Off" in config.yml.
  3. Add libapache2_mod_php5 to extra_packages in config.yml.
  4. For the Drupal VM site in apache_vhosts, remove or comment out the ProxyPass directive since we don't want to use mod_proxy to load the site, but rather mod_php.
  5. After boot, vagrant ssh in and run a2dismod deflate (to disable gzip) and then restart Apache with sudo service apache2 restart.

Testing BigPipe in Drupal 8

The easiest way to test BigPipe in Drupal 8 is to install the BigPipe demo module.

You can also test PHP streaming (to make sure it's working correctly) using the PHP script pasted in the comments on this issue.

@geerlingguy geerlingguy added the planned label Mar 23, 2016

@geerlingguy geerlingguy added this to the 3.0.0 milestone Mar 23, 2016

@geerlingguy

This comment has been minimized.

Owner

geerlingguy commented Mar 23, 2016

Another important note: Apache's mod_proxy_fcgi doesn't support streaming for BigPipe, so testing inside Drupal VM's default configuration can only be done against Nginx at this time (unless you want to go back to using crufty old mod_php), and for Nginx, you have to set fastcgi_buffering to off.

This setting would need to be added inside https://github.com/geerlingguy/drupal-vm/blob/master/provisioning/templates/nginx-vhost.conf.j2#L29-L38 — should we default it to on or off though...?

@geerlingguy

This comment has been minimized.

Owner

geerlingguy commented Mar 23, 2016

And for Varnish, it seems the only change required would be:

sub vcl_backend_response {
  ...
  # Disable buffering only for BigPipe responses.
  if (beresp.http.Surrogate-Control ~ "BigPipe/1.0") {
    set beresp.do_stream = true;
    set beresp.ttl = 0s;
  }
}
@geerlingguy

This comment has been minimized.

Owner

geerlingguy commented Mar 25, 2016

One issue fixed upstream: geerlingguy/ansible-role-php#99 — you can now set php_output_buffering to Off to disable it.

@geerlingguy

This comment has been minimized.

Owner

geerlingguy commented Mar 26, 2016

For testing (quicker than doing everything inside Drupal), I'm trying to just verify that Nginx is passing through the stream correctly... so far it seems to not be doing so.

Put this script (named test.php) inside the drupal folder:

<?php
// Set a valid header so browsers pick it up correctly.
header('Content-type: text/html; charset=utf-8');

// Emulate the header BigPipe sends so we can test through Varnish.
header('Surrogate-Control: BigPipe/1.0');

// Explicitly disable caching so Varnish and other upstreams won't cache.
header("Cache-Control: no-cache, must-revalidate");

// Setting this header instructs Nginx to disable fastcgi_buffering and disable
// gzip for this request.
header('X-Accel-Buffering: no');

echo 'Begin test...<br />' . "\r\n";

// For 3 seconds, repeat a 1024 byte string.
for ($i = 0; $i < 3; $i++) {
  // 1024 byte string.
  $one_kilobyte_string = str_repeat('.', 1024);
  echo $one_kilobyte_string . '<br />' . "\r\n";
  echo $i . '<br />' . "\r\n";
  flush();
  sleep(1);
}

echo 'End test...<br />' . "\r\n";
?>

Then run it within Drupal VM using PHP directly: php /var/www/drupalvm/drupal/test.php. It should show a progressive output with no buffer.

Then load it via http://drupalvm.dev/test.php. Currently with the configuration I'm testing, I can't get Nginx to flush it immediately; it caches then flushes the buffer at the end.

@geerlingguy

This comment has been minimized.

Owner

geerlingguy commented Mar 26, 2016

And I was monitoring the packets being sent from Nginx to PHP-FPM using tcpdump -nn -i any -A -s 0 port 9000; when I made a request, I could see the packets being sent realtime to Nginx, so Nginx is definitely buffering here... grr.

@geerlingguy

This comment has been minimized.

Owner

geerlingguy commented Mar 26, 2016

It gets more annoying... somewhere, something is buffering something, and it's getting annoying figuring out where this buffering is happening.

If I run curl localhost/test.php inside the VM, I see the test script output render live (flushing after each chunk, as long as it's 1024 bytes or more—if it's < 1024 bytes, the whole response is chunked and flushed at the end... this is with Nginx or Apache mod_php5/prefork).

If I run curl http://drupalvm.dev/test.php from outside the VM (with or without the default firewall rules in place), then curl doesn't print anything until the entire response is returned from Apache.

So there's some funkiness going on at the Apache layer (the minimum of 1024 bytes), plus some strange behavior with requests that go through the VM from the host OS.

Next up, tested with BigPipe on the Drupal 8.1.0 site, with the results:

  • Test from inside VM (`curl 'http://drupalvm.dev/' -H 'Cookie: SESSxyz=XYZ'``): works; I can see the bigpipe placeholdering!
  • Test from outside VM (same exact command): doesn't work (nothing returned until the whole page is returned).
@geerlingguy

This comment has been minimized.

Owner

geerlingguy commented Mar 26, 2016

So far, besides the config.yml changes in the OP above, the varnish VCL change in the commit above is the only other thing required. Tested and working so far.

@geerlingguy

This comment has been minimized.

Owner

geerlingguy commented Mar 28, 2016

I'm going to rebuild Drupal VM in VirtualBox and see if the same issue persists. I'm guessing it's something with VMware's network adapter/NAT...

@geerlingguy

This comment has been minimized.

Owner

geerlingguy commented Mar 28, 2016

Works great in VirtualBox! VMware Fusion--... I wonder what's causing the #networkingfail

@geerlingguy

This comment has been minimized.

Owner

geerlingguy commented Mar 28, 2016

A few notes, as well (just because I want to make sure to remember these points, even though they're more obvious in hindsight):

  • BigPipe won't make placeholdered content load asynchronously, like you can do via ESI (server side) or HInclude (client side); there is only one PHP process and one connection between client/server. And it's synchronous.
  • BigPipe doesn't make your page load faster—but it makes the perceived load faster when you have bits of content that take a longish amount of time to render.
  • BigPipe is not helpful for brochureware sites or sites where there is little or no authenticated traffic (this should be obvious, but I think many people don't realize most websites aren't Facebook :) ).
  • BigPipe still relies on the dynamic page cache to cache other parts of the page for the fast initial response; so the first time you hit a new page that's using BigPipe, the page will not render until the last of the content is returned—but after that Drupal should have all the cacheable bits in its dynamic page cache with the placeholders ready. At that point, you can see the power of the fully armed and operational BigPipe.

@geerlingguy geerlingguy added the docs label Mar 28, 2016

@geerlingguy

This comment has been minimized.

Owner

geerlingguy commented Mar 28, 2016

At this point it's just a documentation issue; I'm going to document the two ways to use BigPipe and put a big fat bold warning about not trying to use it with VMware Fusion... though I want to get to the bottom of why Fusion buffers things and doesn't allow streaming!

@geerlingguy

This comment has been minimized.

Owner

geerlingguy commented Mar 29, 2016

What... the...? I just recreated the VM again with VMware, and now everything is working fine. So strange. Looks like it must've been some strange networking issue on my Mac, maybe from VMware, maybe from the network I was on when I brought up that VM originally... maybe the VPN software on the Mac I was using, who knows.

But now I can get BigPipe working with Nginx/Varnish/Apache on Drupal VM on VMware Fusion or VirtualBox, reliably. Time to add docs!

@geerlingguy geerlingguy changed the title from Support BigPipe streaming in Varnish VCL template to Support BigPipe streaming in Drupal VM - Varnish VCL and docs update Mar 29, 2016

@geerlingguy

This comment has been minimized.

Owner

geerlingguy commented Mar 29, 2016

I'm hoping to post two blog posts—one on PHP and HTTP request streaming/buffering in general (to expand a bit on the environment requirements on Drupal.org and PHP streamed responses in general), and the other on BigPipe and Drupal VM.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment