Massive Magento Attributes
Published: March 27, 2015
Magento is often criticized for being slow. I won't lie, the first time I used Magento that was my reaction as well. But after more than a year working at a Magento Gold Solution Partner I've learned that with the right hardware, and software, Magento can run rather smoothly...most of the time.
The story starts with an issue reported by a client...
When I try access the manufacturer attribute I get an unresponsive script error
The screenshot looked something like this...
So I take a look at the manufacturer attribute. It's a dropdown attribute with around 3,500 options. While I'm not experiencing the unresponsive script error it's definitely sluggish. Some research is in order.
What They Say On The Interwebz
Some quick Googling brought me to this blog post. After reading through the post and quicky reviewing the proposed solution my initial reaction was go with the module created to handle this problem. However, after discussing with a colleague I decided to dig into the issue myself. Below are my findings.
What's The Problem?
Note: Below is based on investigation of the issue in 22.214.171.124. YMMV.
If you'd like to follow along, for starters, you'll need a massive attribute for testing. You can use the following MySQL script to create said attribute...
Once you have your attribute, go there in Firefox. The issue was reported in Firefox and I actually found the Firefox developer tools (not Firebug) to be extremely useful for debugging this issue. Go head and pop open Firefox developer tools and open the "Performance" tab. Click record and then back in your browser click into "Massive Attribute" attribute. Stop the recording when the page loads. Here's what I got. Anything standing out to you here?
One thing certainly stand out to me. 20 out of the 22 seconds required to load the page were spent on attributeOption.bindRemoveButtons. I think we're on to something!
To get started crack open app/design/adminhtml/default/default/template/catalog/product/attribute/options.phtml. That is the template responsible for this page:
Spend a little to and try to grok this file.
OK now let's break it down...
At the bottom of the file, options are added to the page through the following foreach loop
Here's the add method for the attributeOption object
So bind bindRemoveButtons is getting called each time an option is added. Let's take a look a bindRemoveButtons...
And there's the glaring inefficiency.
bindRemoveButtons is checking every single remove button element on the page each time a new option is added rather than just checking the new option that was added. With 3,500 you could see how this could add some overhead head!
OK, now that we have a better idea what's going on let's blow this popsicle stand.
The easiest solution is to take bindRemoveButtons out of the add method and then call it once after all remove buttons have been added to the page. Let's try that and check the performance tab again in Firefox. Here's what I got...
Well would you look at that! bindRemoveButtons went from taking 20 seconds to taking less than 1 second on my machine. I'd say that's a pretty nice improvement.
As I said in the start, Magento can definitely whirr if tuned properly. Fortunately this is an admin only issue and won't have any customer facing impact. That being the issue impacts end users frequently enough that I'm not the only one who's written about it. Looking at the Magento 2 source code it looks like this is still an issue. Until this get's patched in the Magento core (In the process of writing this I've decided that I'd like to submit a PR) make sure to patch this template if you'd like to prevent some headaches that admins of large scale stores will run into.