Magento2 - Benchmark fixtures

Magento2 - Benchmark fixtures

October 9, 2021

Introduction #

With release of Magento2 - developers got a nice tool called - Performance Toolkit. Its purpose is defined clearly:

The Performance Toolkit enables you to test the performance of your Magento installations and the impact of your customizations. It allows you to generate sample data for testing performance and to run Apache JMeter scenarios, which imitate users activity.

However, Magento developers use just fraction of it’s possibilities - that is - creation of fake data. Probably everyone knows command:

bin/magento setup:performance:generate-fixtures profile.xml

This command will automatically create set of products, categories, images and content from all areas automatically. It is easier and faster to generate such dummy data than installing sample data provided by Magento. You can also define how large it should be - which is useful if you know your customization will have to work against large catalogs or customer base - in this case you are able to measure and track performance issues already during development process.

After command is done, you can see your shop filled with products, categories and so on. Notice, that even fake images are available:

alt tex2t

How does it work? #

GenerateFixtureCommand is part of base Setup module of Magento, so it’s content is located in setup/ directory.

Command itself is located in setup/src/Magento/Setup/Console/Command/GenerateFixturesCommand.php

This command takes two options:

  • profile to use - which is XML file with all required configuration options - which define options for each Fixture model. For example - for simple products you can define how many products should be created.
  • skip reindex option (-s) - to skip full reindex after all fixtures are executed.

There are few profiles already available out of the box, for ce and ee versions in different directories. You can find them in:

setup/performance-toolkit/profiles/ce/extra_large.xml

There are 5 versions available, ranging from small to extra large datasets. Extra large one will create 600 000 products and 150 000 orders! So be careful when choosing correct profile, because for local development it can be huge overkill for your machine. You are also enouraged to create a copy of this file and adjust it just to your needs.

Loading and executing fixtures #

Command searches for definex fixtures by scanning recursively current directory for files containing *Fixture.php keyword. Then all the files are being read and object of class Magento\Setup\Fixtures\<FILENAME> is instantiated. Each class must contain information about priority - it defines orders in which fixtures will be executed.

💡 There are few rules:

  • priority must be greater than 0;
  • pririties must be unique - having two or more fixtures with same priority will result in exception;
  • lower priorities are executed first (kinda strange!)

Now, all fixtures are loaded and command is ready to move forward.

First, printInfo() method is executed. This method of each Fixture class should return some brief information about what it’s gonna actually do - with information about crucial configurations.

This method is executed on each fixture, and here comes the result of it:

alt tex2t

⬆️ This is just description of things that will be created, it’s shown before actual execution.

As next steps - fixture for applying configuration changes (ConfigsApplyFixture) is applied.

It’s executed always as first fixture, since configurations are very basic things to run.

You can see (and also define your own changes) in profile XML:

<configs>
    <config>
        <path>admin/security/use_form_key</path>
        <scope>default</scope>
        <scopeId>0</scopeId>
        <value>0</value>
    </config>
    <config>
        <path>admin/security/session_lifetime</path>
        <scope>default</scope>
        <scopeId>0</scopeId>
        <value>7200</value>
    </config>
    ...
💡 This can be useful for very basic automation of setting up new project - where you can define config settings convenient for development. All it takes is to execute fixtures, which you’re gonna do anyway.

Now, before applying remaining fixtures - one small and crucial thing is done concerning indexers. Since we are about to generate thousands of entities - we must ensure that all indexers are set to on schedule mode. Otherwise, every save of product, category, etc - would create reindex on save, which would significantly increase time of operation. So - previous state of indexers is first saved in array, then mode is changed to to on schedule. After execution of all fixtures, indexers will have their state restored.

Now, the crucial part is actually the least interesting. In a loop of all fixtures, first $fixture->getActionTitle() is executed - which defines fixture’s title, for example: Generating websites, stores and store views... - this is message that will be outputted to screen when command is running. Then - method $fixture->execute($output); is invoked, where all the logic for each fixture is defined.

💡 Please note, that Symfony\Component\Console\Formatter\Output is passed to this method, so you are able to provide additional information when fixture is being executed (for example show progress bar).

You may wonder - but if indexers have been set to on schedule mode during execution, there are probably thousands of entries in changelog tables, waiting to be processed - and that’s true. Therefore - what Magento does - it calls clearChangelog() method which gets configuration of Mview tables and simply truncates them. Then, full reindex is done. Smart!

<?php
private function clearChangelog()
    {
        $viewConfig = $this->fixtureModel->getObjectManager()->create(CollectionInterface::class);

        /* @var ResourceConnection $resource */
        $resource = $this->fixtureModel->getObjectManager()->get(ResourceConnection::class);

        foreach ($viewConfig as $view) {
            /* @var \Magento\Framework\Mview\ViewInterface $view */
            $changeLogTableName = $resource->getTableName($view->getChangelog()->getName());
            if ($resource->getConnection()->isTableExists($changeLogTableName)) {
                $resource->getConnection()->truncateTable($changeLogTableName);
            }
        }
    }

Summary #

Fixtures feature in Magento2 is very handy for each developer. I use it mainly for:

  • filling up content on fresh installations of Magento
  • setting up default config values which I like during development (session lifetime, admin password change requests, etc.)
  • extending to create custom entities for features I develop - it’s much faster than writing own sample-data module.
  • having same dataset for performance benchmarks, to see impact of my custmizations.

🍺 If you liked this article you might consider buying me a beer? ;)