Quite recently we, meaning PrepLounge, the startup I’m CTO at, got started on a new project and I chose Symfony as the framework to work with.
After reading the docs and running through the, fairly simply, install/setup steps I ran into a problem: Performance was really bad. A single request to the default controller (no changes where made to the AppBundle) took up to 8 seconds. This was unacceptable.
I googled about Symfony being slow and followed all the steps to improve the performance like:
- Use bootstrap files
- Enable APC for autoloading
- Use composer’s class map functionality
Which is pretty much the same that’s listed on Symfony’s own site about performance improvements. And I even disabled Xdebug (on my dev system mind you, something unthinkable until now) in hopes that this might be the culprit. But all to no avail. Sure, every improvement shaved off a couple hundred milliseconds, but the best result I got was about 2 seconds per request. Which was still unacceptable.
I then dug deeper into the Symfony docs, trying to figure out how all the caching works and after reading how much file reading/writing is done for each request I started to suspect a completely different reason for the weak performance: My VM I use development.
My VM is a VirtualBox with Ubuntu 14.04 as guest system running on my MacBook Pro with OSX 10.11.1 as host. For all previous projects, the code always resided on the host and the VM gets access to it via shared folders.
And that’s exactly the problem. Apparently file reads/writes in shared folders are very slow compared to reads/write on the local file system. I had no idea. Sure, I figure there’d be some impact, but not that much.
Anyway, now that I had my suspicions I made some changes to my setup.
Previous setup:
- Shared folder at /media/sf_project
- Symlink from /media/sf_project to /opt/www/development/project
- DocumentRoot at /opt/www/development/project/web
New setup
- Shared folder at /media/sf_project
- Actual copy of /media/sf_project at /opt/www/development/project
- Updated every minute per rsync
- DocumentRoot at /opt/www/development/project/web
And that did it!! Requests are blazing fast and take less than 100ms!
The only problem is, that the fastest a cronjob can run is once per minute. Which is probably fine in most cases, but for me as a developer to wait up to one minute for my changes to take effect is a nightmare! Especially when you are debugging or editing your CSS and only make small changes which you quickly want to check.
So I wrote a quick script that does exactly what the cronjob does: Run rsync to sync the folders and then set permissions. And then I updated the cron to run the script instead of having duplicate code. DRY ftw ;)
Here’s the script:
#!/bin/bash if [ "$1" == "" ]; then echo "Syncing project files..." sudo rsync -rptz --stats --exclude 'app/logs/*' --exclude 'vendor/*' --exclude 'app/cache/*' /media/sf_project/* /opt/www/development/project sudo rsync -rptz --stats /opt/www/development/project/app/logs /media/sf_project sudo cp /opt/www/development/project/composer.json /media/sf_project sudo cp /opt/www/development/project/composer.lock /media/sf_project echo "Setting permissions..." else sudo rsync -rptz --exclude 'app/logs/*' --exclude 'vendor/*' --exclude 'app/cache/*' /media/sf_project/* /opt/www/development/project sudo rsync -rptz /opt/www/development/project/app/logs /media/sf_project sudo cp /opt/www/development/project/composer.json /media/sf_project sudo cp /opt/www/development/project/composer.lock /media/sf_project fi sudo chown -R www-data:www-data /opt/www/development/project sudo chmod -R 775 /opt/www/development/project
I called it “project-sync”, put it in /usr/local/bin and made it executable
sudo chmod +x /usr/local/bin/sync-project
Now, when calling it you can either simply use
project-sync silent
(actually any value as first parameter will trigger the silent mode) or, if you want more verbose output and info about what happend, simply run
project-sync
.
UPDATE:
I extended the cron to
a) ignore a couple of files I don’t need to copy since they don’t change locally (vendor, logs and cache files) and
b) sync a couple of files back into my local copy that I want to commit/need for debugging like composer-related files and the logs
I hope this is helpful to someone besides me :)
Leave a Reply