ampersand / mage-ee-product-flat-test
用于测试Magento企业版扁平产品索引功能的单元测试。
Requires
- phpunit/phpunit: >=3,<5
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)