-
-
Notifications
You must be signed in to change notification settings - Fork 9.7k
Description
Symfony version(s) affected
6.2.3
Description
I am working on a large Symfony app with approx 1700 @Route
annotations and (so far) 1 #[Route]
attribute, across 1350 files.
XDebug is disabled.
bin/console cache:clear
takes 24 seconds to run. 13 seconds of this is spent in RouterCacheWarmer
.
Almost all this time is spent in \Symfony\Component\Routing\Loader\AnnotationFileLoader::load()
.
I do not have blackfire so I am limited in the profiling I can do (I have been adding hrtime()
around calls to see how long they take).
Why does this matter?
In production it doesn't. In development it's a slowdown in multiple places.
Take a functional test as an example. If I repeatedly run the functional test I pay the cost of the annotations being loaded on the first run. With the fixtures we have, each subsequent run takes ~8 seconds.
If I make any edit to the controller I'm testing, e.g. adding $x = 1;
inside the method body, the routes are reloaded and it takes ~13 seconds, so 5 seconds longer, to rerun the test.
There is something more complex going on because this adds 5 seconds whereas on cache:clear
it's a consistent 13 seconds. I have not got to the bottom of that.
How to reproduce
Add a lot of routes to a project.
Possible Solution
This project uses DDD/CQRS and subclasses \Symfony\Component\Config\Loader\Loader and for local development I have added a permanent annotation cache outside the Symfony cache directory. This removes the performance drag during development but is not a good solution.
<?php
declare(strict_types=1);
namespace Example;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\RouteCollection;
class DddLoader extends Loader
{
private string $srcDir;
public function __construct(KernelInterface $kernel)
{
$this->srcDir = $kernel->getProjectDir()."/src";
// My extra hack
$this->cacheDir = $kernel->getCacheDir().'/../annotations';
@mkdir($this->cacheDir, 0777, true);
}
public function load($resource, string $type = null): RouteCollection
{
$finder = new Finder();
$finder->in($this->srcDir)->name('*Controller.php');
$routes = new RouteCollection();
foreach($finder AS $fileInfo){
$hash = md5($fileInfo->getRealPath());
if (file_exists($this->cacheDir . "/$hash.php") && filemtime($this->cacheDir . "/$hash.php") > $fileInfo->getMTime()) {
$routes->addCollection(unserialize(file_get_contents($this->cacheDir . "/$hash.php")));
} else {
$annotationLoader = $this->resolver->resolve($fileInfo->getRealPath(), "annotation");
if($controllerRoutes = $annotationLoader->load($fileInfo->getRealPath())){
file_put_contents($this->cacheDir . "/$hash.php", serialize($controllerRoutes));
$routes->addCollection($controllerRoutes);
}
}
}
return $routes;
}
public function supports($resource, string $type = null): bool
{
return ($type == "ddd");
}
}
Short of switching from annotations to another format, which I do not have the power to do, are there any improvements that can be made to Symfony to improve performance?
Additional Context
No response