Skip to content

WIP : Msi stock indexing #48#49

Merged
naydav merged 33 commits intodevelopfrom
msi-stock-indexing
Sep 1, 2017
Merged

WIP : Msi stock indexing #48#49
naydav merged 33 commits intodevelopfrom
msi-stock-indexing

Conversation

@larsroettig
Copy link
Copy Markdown
Member

No description provided.

@larsroettig
Copy link
Copy Markdown
Member Author

@maghamed hi it is only basic version I think must also implement a bunching but the code is working some can be optimized. Have you a good example how can I test it?

@larsroettig larsroettig changed the title WIP : Msi stock indexing WIP : Msi stock indexing #48 Jul 30, 2017
@larsroettig larsroettig mentioned this pull request Jul 30, 2017
Copy link
Copy Markdown
Contributor

@maghamed maghamed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Big Merchants, who have a lot of SKUs and a lot of Sales channels (scopes in which products would be sold). We want to prevent a multiplication of data in the index table :
SKUs Qty * Number of Sales Channels

Because that could introduce performance impact both on Read and Write index operations.

That's why it's proposed to split indexes by the Scope.
Taking into account that we have 1 - 1 relation between Scope (Sales Channel) and Stock.
We could Build independent index table per each Stock (Stock_id).

Stock Id could be used as Dimension which will help us to resolve correct index we need to refer to get Product Qty.

For example, having 5 websites with ids (1 .. 5)
we will end up creating 5 different index tables (with the same structure):

inventory_stock_item_stock_1
inventory_stock_item_stock_2
inventory_stock_item_stock_3
inventory_stock_item_stock_4
inventory_stock_item_stock_5

You can refer for similar implementation in the CatalogSearch Index
Magento\CatalogSearch\Model\Indexer\Fulltext
Also, we have a requirement to have index aliasing during the full reindex happened, so we need to have switchable indexes to make Front-End responsive while Full Reindex happen.

Also, please take into account that when the SourceItem data change -> we need to upgrade Stock Item Indexes for each of the Stock to which given Source was assigned.
If Source was assigned to 3 stocks, for example, we need to refresh data in the 3 StockItem indexes

Looks like, for this operation we need to introduce new service
Magento\InventoryApi\Api\GetAssignedStocksForSourceInterface::execute($sourceId);

use \Psr\Log\LoggerInterface;
use \Magento\Framework\App\ResourceConnection;

abstract class AbstractAction
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't introduce AbstractAction class, as Magento doesn't recommend to use Inheritance in newly created code.

Create a dedicated indexer model which would be injected via DI into each of the class:

Row
Rows
Full

and make each of these classes to proxy call to that indexer model

public function __construct(ResourceConnection $resource, LoggerInterface $logger)
{
$this->resource = $resource;
$this->connection = $resource->getConnection();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not recommended to make anything except assignment in the constructors.
Like in your case where you do
$this->connection = $resource->getConnection();

/**
* Stock item Indexer @todo
*/
class StockItem implements \Magento\Framework\Indexer\ActionInterface, \Magento\Framework\Mview\ActionInterface
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's wrong to Make indexer implementing both of these interfaces, because these interfaces responsible for different things. Doing so we violate Single Responsibility Principle.

We need to have two different classes, each one implementing own interface.

Like in CatalogSearch module
https://github.com/magento/magento2/blob/develop/app/code/Magento/CatalogSearch/Model/Indexer/Mview/Action.php
implements \Magento\Framework\Mview\ActionInterface
which is declared in - https://github.com/magento/magento2/blob/develop/app/code/Magento/CatalogSearch/etc/mview.xml#L9

and Magento\CatalogSearch\Model\Indexer\Fulltext implements only \Magento\Framework\Indexer\ActionInterface
which is declared in
https://github.com/magento/magento2/blob/develop/app/code/Magento/CatalogSearch/etc/indexer.xml#L9

Let's do the same way

$indexItems = $this->fetchIndexItems($sourceItems);
if (is_array($indexItems) && count($indexItems) > 0) {
$this->cleanIndexTable($sourceItems);
$this->updateIndexTable($indexItems);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to introduce implementation of IndexerInterface
https://github.com/magento/magento2/blob/develop/lib/internal/Magento/Framework/Indexer/SaveHandler/IndexerInterface.php

and use it for the operations of clean and delete index


use Magento\InventoryApi\Api\Data\StockItemInterface;

class CreateStockItemIndexTable
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be implemented similarly to what we have in CatalogSearch
where
Magento\CatalogSearch\Model\Indexer\IndexStructure
responsible for creating StockItem index structure.

IndexStructure implements interface
Magento\Framework\Indexer\IndexStructureInterface

we will reuse this class for index structure creation because we will create dedicated index table for each particular Stock (stock_id). To make the system being scalable horizontally.
As each index table will hold StockItems for each particular Scope (SalesChannel).

For example, having 5 websites each of which represents Sales Channel we will end up with 5 index tables:

inventory_stock_item_index_1 - for website_id 1 
inventory_stock_item_index_2 - for website_id 2
inventory_stock_item_index_3 - for website_id 3
inventory_stock_item_index_4 - for website_id 4
inventory_stock_item_index_5 - for website_id 5

try {
$this->reindexRows($ids);
} catch (\Exception $e) {
throw new \Magento\Framework\Exception\LocalizedException(__($e->getMessage()), $e);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not correct translation,
__($e->getMessage()), $e);
Because \Exception $e ,so it's not a successor of Localized Exception, thus $e->getMessage()
could return a string for which you don't have precise translation

try {
$this->reindexRows([$id]);
} catch (\Exception $e) {
throw new \Magento\Framework\Exception\LocalizedException(__($e->getMessage()), $e);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not correct translation,
__($e->getMessage()), $e);
Because \Exception $e ,so it's not a successor of Localized Exception, thus $e->getMessage()
could return a string for which you don't have precise translation

*
* @return void
*/
public function execute($id = null)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we allow to accept NULL value if we throw an exception if NULL passed as a parameter?

* Interface StockItemInterface
* @api
*/
interface StockItemInterface extends ExtensibleDataInterface
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now we don't have a Service API to retrieve Stock Items for a given Stock_ID

That's why for now we can't get StockItems in any case
These API should be introduced to InventoryApi\Api\StockItemManagerInterface

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Indexer/etc/indexer.xsd">
<indexer id="inventory_stock_item" view_id="inventory_stock_item" class="Magento\Inventory\Model\Indexer\StockItem">
<title translate="true">StockItem</title>
<description translate="true">Index stock item</description>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide more clear description

Copy link
Copy Markdown
Contributor

@maghamed maghamed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general everything good, @larsroettig please keep going

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to make these fields as constants, because constants are the contract which could be accessible outside of the class (like public methods)

it's better to make them private static in this case, so, no one from outside could rely on these values.
And we will not duplicate these data for all instantiated classes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's better to use IndexStructureinterface here and specify in DI Type what exact implementation should use here

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use DI Type configuration for batchSize, especially taking into account that you will use Type for IndexStructure

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm... it's better to keep this constant away from the concrete implementation, but for now I don't see a place where we could move it

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how can we set these data?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's introduce

interface StockItemIndexerInterface extends \Magento\Framework\Indexer\ActionInterface
{
    /**
      * Indexer ID in configuration
      */
     const INDEXER_ID = 'inventory_stock_item_index';
}

and make this class to implement this interface.

But Ideally to have getName method in ActionInterface which returned value initialized from the configuration, which we can't add because of BackwardCompatibility

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we have StockItemManager which behaves like DataInterface,

Manager means to make some actions over some data.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need this DataInterface?
it duplicated StockItemInterface, but called manager and never used

looks like a mistake

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cast to int is extra

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed parameter type

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pls delete or add more description to @todo annotation

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move default value to di configuration

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to more clear name?
What is mean data?

Copy link
Copy Markdown

@naydav naydav Aug 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As for me *Manager is not good name for "entity"
Maybe it is better simple "StockItem"

But pay attention we already have \Magento\InventoryApi\Api\Data\StockItemInterface

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is not stock item sku

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

visa versa
Get Stocks assigned to Source

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to process disabling/enabling of products?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update doc block.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please describe constants.
More info is available in the devdocs

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a type hint

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a type hint

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to have a separate namespace for Indexer. Not to put all indexers to
Magento\Inventory\Model\*

Let's create dedicated namespace like

Magento\Inventory\Indexer\

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the documentation https://github.com/magento-engcom/magento2/wiki/New-Indexer-for-StockItems#index-dimensions stated

That's why it's proposed to split indexes by the Scope. Taking into account that we have 1 - 1 relation between Scope (Sales Channel) and Stock
For example, having 5 websites with ids (1 .. 5) we will end up creating 5 different index tables (with the same structure):

inventory_stock_item_stock_1
inventory_stock_item_stock_2
inventory_stock_item_stock_3
inventory_stock_item_stock_4
inventory_stock_item_stock_5

Current code where dimension is StoreId is copy-pasted from Search Indexer

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we already have
\Magento\Framework\Search\Request\Dimension
why do we create own Dimension object?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pls provide more clear class name
class name is proxy, class interface is resolver, due to code it is factory... it is not clear what is responsibility of this class

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to create a new object each time?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is better to place in State object. In this way, we don't provide possibility to set the wrong state

But if we look code of State object, we can not find possibility to set the wrong state.
In State objectwWe have only useTemporaryIndex and useRegularIndex methods, thus we can not set state different from these two.
So this checking looks like redundant

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree with @naydav in the scope of UnknownStateException I described the idea of State Machine, so don't think that we need to check the state and have a factory for state creation

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that is taken from search index but I don't like naming (TemporaryResolver, IndexScopeResolver, ScopeProxy)
@maghamed what do you think about this naming?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@naydav makes sense, naming should be aligned to outline the best practices and make it understandable for external developers what's the responsibility of the class reading its name.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to think about moving this exception to framework lib if we will use it

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@naydav agree, already pointed that out for IndexTableNotExistException.
These should be framework level exceptions, we can't provide them to client because they provide details of how this functionality is implemented (leaky abstractions).

So, these exceptions are not successors of Localized

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, I don't know why we have this Exception in our codebase.
So, from what I see we have a pre-defined finite list of states which could be switched in pre-defined order. (aka State machine design pattern)

  • our state object initially being initialized in 'REGULAR' state.
  • after that, we've decided to move it to 'TEMPORARY' state while making re-indexation
  • after re-indexation complete - we move the object to 'REGULAR' state again.

Is it correct? I suppose so, then why do we have a possibility that State object would be created in a wrong state?
@larsroettig

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it is better to use one fixture with all preconditions

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you create an instance of repository each time?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can reuse fixture from catalog module

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have this fixture
app/code/Magento/InventoryApi/Test/_files/source/source_list.php

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

app/code/Magento/InventoryApi/Test/_files/source/stock_list.php

larsroettig added 17 commits August 22, 2017 17:00
- Add Setup for index table
- Add new Interface"
- Refactoring to new Setup structure
- Implement SourceItem add entry to di.xml
- WIP Register Basic Indexer SourceItem
- Impelemnt basic import funktion

TODOS:
- Add some bunching function for index
- Add Mysql Index to  inventory_stock_item_index table
- Documentation and clean up
- Clean Up and implement basic structure
- Impelemnt basic import function

TODOS:
- Index switch from tmp to idx table
- Documentation and clean up
- Add Setup for index table
- Add new Interface

I magento-engcom/magento#48:
- Add Setup for index table
- Add new Interface
- Implement switch for Index

TODO:
- Clean Up and more Doc
- Discuss the missing website to stock mapping
- Impelemnt Basic intigration test fixtures
- Bugfix for di.xml
- Add basic call for reindex full
- Basic Indexer with switch for tmp and normal index

TODO:
- Refactoring to remove the state folder from the module
- Doc blocks and clean up
- Add assertions to the test case
-- fixes after merge
@naydav naydav force-pushed the msi-stock-indexing branch from 23864df to 31e5df0 Compare August 22, 2017 14:10
@magento-cicd2
Copy link
Copy Markdown

magento-cicd2 commented Aug 22, 2017

CLA assistant check
Thank you for your submission, we really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ larsroettig
❌ naydav

Copy link
Copy Markdown
Contributor

@maghamed maghamed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@naydav please help @larsroettig
and join this story development.

It's very sophisticated Story, and there are no good examples in Magento where re-indexation was implemented totally correct.

So, let's make a good example of introducing New Index to the Magento 2. Outlining all the steps and document them afterwards.

We already agreed that ActiveTableSwitcher would be moved to Framework maybe we need some additional things to move to the framework level?

* @todo refactoring it copy from catalog search module
* @api
*/
class IndexTableNotExistException extends LocalizedException
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are introducing IndexTableNotExistException, this exception should be low-level (framework level).
Not inherited from LocalizedException.
Because we can't provide this Exception o the client code, even its name is a leaky abstraction of the internal details.

@@ -0,0 +1,24 @@
<?php
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Magento there is another Switcher mechanism - https://github.com/magento-engcom/magento2/blob/develop/app/code/Magento/Catalog/Model/ResourceModel/Indexer/ActiveTableSwitcher.php

by our request, it would be moved to the Framework soon (in the scope of Magento 2.2.0 regression).
So, we could consider using ActiveTableSwitcher for our purposes.

@naydav please consider this and provide a solution for @larsroettig

maybe we need some additional classes to be moved to the Framework?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@naydav makes sense, naming should be aligned to outline the best practices and make it understandable for external developers what's the responsibility of the class reading its name.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@naydav agree, already pointed that out for IndexTableNotExistException.
These should be framework level exceptions, we can't provide them to client because they provide details of how this functionality is implemented (leaky abstractions).

So, these exceptions are not successors of Localized

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, I don't know why we have this Exception in our codebase.
So, from what I see we have a pre-defined finite list of states which could be switched in pre-defined order. (aka State machine design pattern)

  • our state object initially being initialized in 'REGULAR' state.
  • after that, we've decided to move it to 'TEMPORARY' state while making re-indexation
  • after re-indexation complete - we move the object to 'REGULAR' state again.

Is it correct? I suppose so, then why do we have a possibility that State object would be created in a wrong state?
@larsroettig

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree with @naydav in the scope of UnknownStateException I described the idea of State Machine, so don't think that we need to check the state and have a factory for state creation

larsroettig and others added 15 commits August 26, 2017 23:05
- Add test for index row
# Conflicts:
#	app/code/Magento/Inventory/etc/di.xml
- refactoring
- refactoring
- refactoring
- refactoring
- refactoring
- refactoring
- refactoring
- refactoring
- refactoring
- fix static tests
- update db structure
- fix static tests
@naydav
Copy link
Copy Markdown

naydav commented Sep 1, 2017

Internal build
https://bamboo.corp.magento.com/browse/M2-AT2478-3

  • Internal static build (INTEGRITY-SANITY-EE) is red due to MAGETWO-71596
  • Internal static build (Static LEGACY-EE-L64 and EE-L64) is red.
    But coupling between objects for \Magento\Inventory\Test\Integration\Indexer\StockItemTest will be fixed during https://github.com/magento-engcom/magento2/issues/69

@maghamed
Copy link
Copy Markdown
Contributor

maghamed commented Sep 1, 2017

Approved to be merged into the Develop branch

Great job guys @larsroettig @naydav , well done!

@naydav naydav merged commit b5c9550 into develop Sep 1, 2017
@vrann vrann added msi and removed msi labels Sep 29, 2017
@maghamed maghamed deleted the msi-stock-indexing branch December 11, 2018 18:12
magento-devops-reposync-svc pushed a commit that referenced this pull request Jun 5, 2025
ACP2E-3863: [Bundle 12] Update copyrights for MSI and fix static errors.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants