Skip to content

Commit

Permalink
Merge pull request #333 from magento-fearless-kiwis/MAGETWO-55589-bat…
Browse files Browse the repository at this point in the history
…ch-size

Fixed issues:
 - MAGETWO-55589: Wrong algorithm for calculation batch size on category indexing
  • Loading branch information
Oleksii Korshenko authored Sep 2, 2016
2 parents 70b8638 + 8263ddb commit 22fee4f
Show file tree
Hide file tree
Showing 6 changed files with 714 additions and 43 deletions.
67 changes: 24 additions & 43 deletions lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use Magento\Framework\Phrase;
use Magento\Framework\Stdlib\DateTime;
use Magento\Framework\Stdlib\StringUtils;
use Magento\Framework\DB\Query\Generator as QueryGenerator;

/**
* @SuppressWarnings(PHPMD.ExcessivePublicCount)
Expand Down Expand Up @@ -199,6 +200,11 @@ class Mysql extends \Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface
*/
private $exceptionMap;

/**
* @var QueryGenerator
*/
private $queryGenerator;

/**
* @param StringUtils $string
* @param DateTime $dateTime
Expand Down Expand Up @@ -3362,57 +3368,32 @@ public function insertFromSelect(Select $select, $table, array $fields = [], $mo
* @param int $stepCount
* @return \Magento\Framework\DB\Select[]
* @throws LocalizedException
* @deprecated
*/
public function selectsByRange($rangeField, \Magento\Framework\DB\Select $select, $stepCount = 100)
{
$fromSelect = $select->getPart(\Magento\Framework\DB\Select::FROM);
if (empty($fromSelect)) {
throw new LocalizedException(
new \Magento\Framework\Phrase('Select object must have correct "FROM" part')
);
}

$tableName = [];
$correlationName = '';
foreach ($fromSelect as $correlationName => $formPart) {
if ($formPart['joinType'] == \Magento\Framework\DB\Select::FROM) {
$tableName = $formPart['tableName'];
break;
}
}

$selectRange = $this->select()
->from(
$tableName,
[
new \Zend_Db_Expr('MIN(' . $this->quoteIdentifier($rangeField) . ') AS min'),
new \Zend_Db_Expr('MAX(' . $this->quoteIdentifier($rangeField) . ') AS max'),
]
);

$rangeResult = $this->fetchRow($selectRange);
$min = $rangeResult['min'];
$max = $rangeResult['max'];

$iterator = $this->getQueryGenerator()->generate($rangeField, $select, $stepCount);
$queries = [];
while ($min <= $max) {
$partialSelect = clone $select;
$partialSelect->where(
$this->quoteIdentifier($correlationName) . '.'
. $this->quoteIdentifier($rangeField) . ' >= ?',
$min
)
->where(
$this->quoteIdentifier($correlationName) . '.'
. $this->quoteIdentifier($rangeField) . ' < ?',
$min + $stepCount
);
$queries[] = $partialSelect;
$min += $stepCount;
foreach ($iterator as $query) {
$queries[] = $query;
}
return $queries;
}

/**
* Get query generator
*
* @return QueryGenerator
* @deprecated
*/
private function getQueryGenerator()
{
if ($this->queryGenerator === null) {
$this->queryGenerator = \Magento\Framework\App\ObjectManager::getInstance()->create(QueryGenerator::class);
}
return $this->queryGenerator;
}

/**
* Get update table query using select object for join and update
*
Expand Down
192 changes: 192 additions & 0 deletions lib/internal/Magento/Framework/DB/Query/BatchIterator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
<?php
/**
* Copyright © 2016 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\Framework\DB\Query;

use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\DB\Select;

/**
* Query batch iterator
*/
class BatchIterator implements \Iterator
{
/**
* @var int
*/
private $batchSize;

/**
* @var Select
*/
private $select;

/**
* @var int
*/
private $minValue = 0;

/**
* @var string
*/
private $correlationName;

/**
* @var string
*/
private $rangeField;

/**
* @var Select
*/
private $currentSelect;

/**
* @var AdapterInterface
*/
private $connection;

/**
* @var int
*/
private $iteration = 0;

/**
* @var string
*/
private $rangeFieldAlias;

/**
* @var bool
*/
private $isValid = true;

/**
* Initialize dependencies.
*
* @param Select $select
* @param int $batchSize
* @param string $correlationName
* @param string $rangeField
* @param string $rangeFieldAlias
*/
public function __construct(
Select $select,
$batchSize,
$correlationName,
$rangeField,
$rangeFieldAlias
) {
$this->batchSize = $batchSize;
$this->select = $select;
$this->correlationName = $correlationName;
$this->rangeField = $rangeField;
$this->rangeFieldAlias = $rangeFieldAlias;
$this->connection = $select->getConnection();
}

/**
* @return Select
*/
public function current()
{
if (null == $this->currentSelect) {
$this->currentSelect = $this->initSelectObject();
$itemsCount = $this->calculateBatchSize($this->currentSelect);
$this->isValid = $itemsCount > 0;
}
return $this->currentSelect;
}

/**
* @return Select
*/
public function next()
{
if (null == $this->currentSelect) {
$this->current();
}
$select = $this->initSelectObject();
$itemsCountInSelect = $this->calculateBatchSize($select);
$this->isValid = $itemsCountInSelect > 0;
if ($this->isValid) {
$this->iteration++;
$this->currentSelect = $select;
} else {
$this->currentSelect = null;
}
return $this->currentSelect;
}

/**
* @return int
*/
public function key()
{
return $this->iteration;
}

/**
* @return bool
*/
public function valid()
{
return $this->isValid;
}

/**
* @return void
*/
public function rewind()
{
$this->minValue = 0;
$this->currentSelect = null;
$this->iteration = 0;
$this->isValid = true;
}

/**
* Calculate batch size for select.
*
* @param Select $select
* @return int
*/
private function calculateBatchSize(Select $select)
{
$wrapperSelect = $this->connection->select();
$wrapperSelect->from(
$select,
[
new \Zend_Db_Expr('MAX(' . $this->rangeFieldAlias . ') as max'),
new \Zend_Db_Expr('COUNT(*) as cnt')
]
);
$row = $this->connection->fetchRow($wrapperSelect);
$this->minValue = $row['max'];
return intval($row['cnt']);
}

/**
* Initialize select object.
*
* @return \Magento\Framework\DB\Select
*/
private function initSelectObject()
{
$object = clone $this->select;
$object->where(
$this->connection->quoteIdentifier($this->correlationName)
. '.' . $this->connection->quoteIdentifier($this->rangeField)
. ' > ?',
$this->minValue
);
$object->limit($this->batchSize);
/**
* Reset sort order section from origin select object
*/
$object->order($this->correlationName . '.' . $this->rangeField . ' ' . \Magento\Framework\DB\Select::SQL_ASC);
return $object;
}
}
51 changes: 51 additions & 0 deletions lib/internal/Magento/Framework/DB/Query/BatchIteratorFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php
/**
* Copyright © 2016 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\Framework\DB\Query;

/**
* Factory class for @see \Magento\Framework\DB\Query\BatchIterator
*/
class BatchIteratorFactory
{
/**
* Object Manager instance
*
* @var \Magento\Framework\ObjectManagerInterface
*/
private $objectManager = null;

/**
* Instance name to create
*
* @var string
*/
private $instanceName = null;

/**
* Factory constructor
*
* @param \Magento\Framework\ObjectManagerInterface $objectManager
* @param string $instanceName
*/
public function __construct(
\Magento\Framework\ObjectManagerInterface $objectManager,
$instanceName = \Magento\Framework\DB\Query\BatchIterator::class
) {
$this->objectManager = $objectManager;
$this->instanceName = $instanceName;
}

/**
* Create class instance with specified parameters
*
* @param array $data
* @return \Magento\Framework\DB\Query\BatchIterator
*/
public function create(array $data = [])
{
return $this->objectManager->create($this->instanceName, $data);
}
}
Loading

0 comments on commit 22fee4f

Please sign in to comment.