Blackfire & Magento

December 31, 2017

The first thing I profiled with Blackfire was a Magento project with PHP 5.5. Like most existing projects where the addition of new features is constant, we started to face performance issues more and more often, while the codebase keeps growing. The project began four years ago.

This article uses several terms related to Blackfire.
If you are not familiar with it, I suggest you read the excellent PHP Code Performance Explained.

Investigations

Blackfire provides several statistics: wall time, I/O wait, CPU time, memory consumption, network, HTTP and SQL queries. Here is my first profile, without page cache and blocks cache. It allows profiling the real output and not the whole Magento initialization.

Toolbar before the fix SQL requests before the fix

You can already see that more than 700 SQL queries are executed on each page only for something related to the URL rewrites system. In the meantime, the call graph shows that the inclusive time within the navigation/top.phtml template is equal to 3.92s.

After further investigations, I found out that the performance issue was due to the wrong usage of a custom method in the main menu generation. Where we were retrieving each category URL to populate HTML links, this custom method loads it from the database systematically.

In our case, the url_path attribute is always exactly equal to the canonical URL. And that attribute is already loaded by default in the categories collection. Thus, the fix is pretty straightforward: access the property via a Magento getter.

And with only these few modified characters, here are the results.

Profile after the fix

Now that the issue is gone, how to be sure that it won’t come back again?

Preventing regressions

It’s common to add a regression by ignoring good practices acquired through experience when a lot of people are working on a project. Especially when team members change often. The code review can protect against these errors, but it’s time-consuming to do it manually. In that case, it would be a good idea to automate these checks.

Blackfire also allows performance testing in addition to profiling. You can use these assertions against all data gathered by Blackfire (time dimensions and even more). During a Blackfire profile, it will look for a .blackfire.yml file in the application root directory. Here is an example with a simple skeleton used for Magento.

You can transpose this example in any projects running on Magento. It’s an excellent start as you can keep the structure and adjust values depending on your project.

In my example, the performance issue was due to the usage of one specific method. Rather than checking if someone incorrectly uses it on every new pull request, you can add an assertion.

Our test suite now contains a new metric and a new assertion. Here is what it looks like from the Blackfire results page based on the tests described above.

Profile with assertions

Afterwords

It’s impressive to see how Blackfire can tell us precisely what is the biggest mistake in a project in less than one hour. But Blackfire does not limit to the manual profiling, and the testing part is a must-have for long-running projects.