ampersand/mage-ee-product-flat-test

用于测试Magento企业版扁平产品索引功能的单元测试。

dev-master 2015-01-27 14:25 UTC

This package is not auto-updated.

Last update: 2024-09-24 15:35:22 UTC


README

Magento应用程序在数据库中维护了多个名为indexes的表,这些表旨在优化前端性能。这些索引表本质上提供了数据库中其他地方数据的简化视图。

Magento企业版维护了多个changelog表。每次创建、更新或删除产品或分类时,产品或分类的ID都会通过MySQL触发器写入changelog表。

每分钟最多一次,通过cron,Magento将从changelog表中读取任何新的ID,并在索引表中重新生成相关的行,确保最新数据在网站的前端可见。

产品扁平索引

Magento使用EAV模式来表示产品数据,这意味着数据存储在许多独立的数据库表中。EAV为商家提供了灵活性,因为他们可以轻松定义自己的产品属性并自定义现有属性,但它也阻碍了性能,因为处理这么多表是复杂的。

为了提高前端性能,Magento有一个名为产品扁平索引的产品数据索引。索引由每个商店的一个单独的表组成,每个表包含每个启用的产品的一行,并为每个产品属性提供一个列。

每次创建或删除产品,或其属性值发生变化时,都会在产品扁平changelog中添加一行或多行,包含已更改产品的ID。每分钟最多一次,该表由Magento读取,并将任何尚未处理的变化传递给产品扁平changelog索引器,该索引器更新受影响产品的产品扁平索引。

以下场景应该会使这个过程更清晰

12:00:00 John executes a product import containing 10 products.
12:00:30 John visits the front-end of the website and finds that his changes have not taken effect.
12:01:00 Magento reads the changelog and re-builds the index rows for those 10 products.
12:01:30 John visits the front-end again and finds that his changes have now taken effect.

技术上,只有产品属性的一小部分出现在扁平表中。

问题

当产品扁平changelog中有超过500个未处理的不同产品ID时,产品扁平索引器只会处理这其中的500个产品,但会将它们全部标记为已处理。因此,产品数据将在前端无限期地显示不正确。

此错误仅影响产品扁平索引,可以追溯到单个方法

Enterprise_Catalog_Model_Index_Action_Product_Flat_Refresh

protected function _reindex($storeId, array $changedIds = array())
{
    ...
    if (!self::$_calls) { // code within this block only executes once per PHP process
        $this->_fillTemporaryEntityTable($entityTableName, $entityTableColumns, $changedIds);
        ...
        $this->_fillTemporaryTable($tableName, $columns, $changedIds);
    }
    ...
    self::$_calls++;
    ...
}

查看上面的代码片段,很明显,在if (!self::$_calls)语句中的代码块只会在一个PHP进程的上下文中执行一次,无论调用_reindex()的次数是多少。然而,$changedIds作为参数传递给方法,并在单次运行的代码块中使用,以'填充临时实体表'和'填充临时表'。因此,那些临时表只使用在_reindex()首次调用中提供的$changedIds来填充,所以除了首次调用之外的所有调用都将无法按预期执行。

在处理扁平产品索引时,Magento企业版将产品ID的积压分成500个产品的批次,并在单个PHP进程中按顺序处理每个批次,如下所述

Enterprise_Catalog_Model_Index_Action_Product_Flat_Refresh_Changelog
  extends Enterprise_Catalog_Model_Index_Action_Product_Flat_Refresh

public function execute()
{
  ...
  $idsBatches = array_chunk($changedIds, Mage::helper('enterprise_index')->getBatchSize());
  
  foreach ($idsBatches as $ids) {
      $this->_reindex($store->getId(), $ids);
  }
  ...
}

从上述代码片段中可以看出,Magento Enterprise意图在单个PHP进程的上下文中多次调用损坏的_reindex()方法,使用不同的产品ID,这是不可行的。

解决方案

最简单的解决方案是删除_reindex()中的静态条件。显然,Magento希望通过一次性执行一次进程的条件来提高性能,但在实践中,这样做只会使通常需要几秒钟才能运行的过程减少几毫秒。最简单的解决方案是删除静态条件,使它在其内部的代码块在每次方法调用时都运行。

证明错误

为了证明这个错误以及任何产生的修复,这个仓库包含了一个PHPUnit测试用例,可以针对纯Magento Enterprise Edition安装运行。这个测试用例已经针对Magento Enterprise Edition >=1.13.0.0, <=1.14.1.0运行,这些版本中都存在这个错误。请注意,1.14.1.0是撰写本文时Magento Enterprise Edition的最新版本。

请注意,这个测试用例会将500多个产品保存到Magento实例的MySQL数据库中,可能需要几分钟才能完成。它不应该在生产实例上执行。

下载PHPUnit测试用例

导航到您的Magento安装根目录,并使用Composer下载测试用例。

composer require ampersand/mage-ee-product-flat-test

执行PHPUnit测试

cd vendor/ampersand/mage-ee-product-flat-test
../../bin/phpunit

测试用例在运行产品扁平索引过程之前创建550个产品。对于每个产品,执行两个断言以检查扁平表是否正确:首先,断言产品存在于扁平表中,其次,断言扁平表中的行包含正确的数据。

当您对纯Magento Enterprise Edition安装执行测试用例时,您应该看到类似于以下失败消息

Time: 3.02 minutes, Memory: 47.50Mb

There was 1 failure:

1) ProductFlatChangelogTest::testMoreThan500Changes
Product with SKU EE-INDEX-BUG-4212650 not found in flat table.
Failed asserting that null is not null.

/ampersand/builds/ee-1.13.1.0/vendor/ampersand/mage-ee-product-flat-test/ProductFlatChangelogTest.php:133
/ampersand/builds/ee-1.13.1.0/vendor/ampersand/mage-ee-product-flat-test/ProductFlatChangelogTest.php:53
                                        
FAILURES!                               
Tests: 1, Assertions: 1001, Failures: 1.

上面的输出告诉我们第1001个断言失败,这意味着前1000个断言成功。鉴于每个产品保存时执行两个断言,这1000个成功的断言代表500个有效的产品,第501个产品是无效的。这个告诉我们索引过程没有正常工作的输出,是当您针对纯Magento Enterprise Edition安装执行此测试用例时的预期输出。

如前所述,从损坏的_reindex()方法中删除静态条件后,测试用例应该可以成功运行并产生类似于以下输出的结果

Time: 3.03 minutes, Memory: 47.50Mb

OK (1 test, 1100 assertions)