4

Context

I'm currently upgrading a site from Drupal 7 to Drupal 8.

In the D7 version, there was a custom "proxy" module which served back-end services. Requests starting with a /service prefix, such as http://my-drupal-domain/service/service-name?service-args, were redirected to a custom index.php inside a D7 module. This alternative index.php called the minimal Drupal code needed : includes and drupal_bootstrap(). Then, the custom module's function handled the request, sometimes using Drupal native functions (to manipulate users for example) and passing it to the target service. Finally, it returned the service response to the client.

Now I want to migrate this to Drupal 8.

I managed to code a custom REST controller, using a custom route :

my_module.rest_services:
  path: '/service/{endpoint}'
  defaults:
    _controller: '\Drupal\my_module\Controller\ProxyController::index'
  methods: [GET, POST, PUT, DELETE]
  requirements:
    _access: 'TRUE'

The ProxyController, which extends Drupal\Core\Controller\ControllerBase, is basically the same code that handled the requests in the previous site, converted to the OOP-way of D8.

Initial problem

My problem is that, using D8 routes and Symfony2 components, the response time for any request increase proportionally with the number of concurrent users compared to the D7 version. For 20 users, this latency is +1s with D8 than with a D7 minimal bootstrap... That's huge! (see chart below, for the exact same call to D7 with a custom minimal bootstrap and D8).

drupal response time

Is there a way to bypass some of D8 code to redirect requests directly to my controller ? Some sort of "minimal D8 bootstrap".

Or a way to improve D8 routes performance ?

Edit: need to cache file parsing

I just discovered Drupal 8 is not the only responsible for my performance loss! during the request handle process, in my module's code, I read and parse a YAML configuration file, using file_get_contents() and Drupal\Component\Serialization\Yaml::decode(). But Drupal does not cache the value by default : the file is read and parsed on each call. With 20 concurrent users, it can take up to 600ms...

I need to find a way to tell D8 "Read this config file once, parse it and cache the content so you don't have to read it again unless your cache is cleared".

  • 1
    Unfortunately, performance is a serious issue with Drupal 8. I have made some benchmarks between 7 and 8 under PHP 7 for mass node creation and the results showed that D7 is 3x times faster than D8. When it comes to serving content to authenticated users, D8 is slower. Those results made me continue developing D7 sites only, never D8. If performance is important and you need API back-ends, I recommend using direct methods of querying DB by-passing Drupal altogether. – W.M. Nov 28 '17 at 16:42
2

Given I did not have many responses, I answer myself to share the bits of explanation and solution I found.

Below the performance loss sources I've identified :

  • The use of D8 full bootstrap, including modules loading, routes resolving, etc.
  • Reading and parsing a YAML config file each time the module handles a request

And the solutions I applied :

Custom D8 minimal bootstrap

Based on this post and the default D8 index.php, I created a custom Bootstrap in my proxy module :

use Drupal\Core\DrupalKernel;
use Symfony\Component\HttpFoundation\Request;
use Drupal\my_module\Controller\ProxyController;

chdir( $_SERVER['DOCUMENT_ROOT'] );

$autoloader = require_once 'autoload.php';

$request = Request::createFromGlobals();
$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod');
$kernel->boot();
$container = $kernel->getContainer();

$response = ProxyController::create( $container )->index( $request );
$response->send();

$kernel->terminate($request, $response);

Like it was made in the D7 version, I redirected all request with the /service prefix to this file.

PHP config instead of YAML

I didn't find a way to load and parse the YAML config file once and cache the result. (I'm still interested if someone know how to do that by the way)

So i converted the config file content myself into PHP, put it into a .php file and use include instead of file_get_contents() and Yaml::decode().

Result

You can see here the response time obtained combining the 2 solutions above.

Drupal response time

I assume the remaining perf loss is due to the use of OOP instead of procedural PHP. But if I want to stick to the D8 standards, I can't do much about it...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.