Caching is one of the most popular ways to optimize the performance of web applications. Magento is not far from this trend and the primary approach to speed up slow code from the core team is to cache it. This approach works well for small projects where the rate of inventory changes is shallow. But as soon as you enter sales season or receive regular product updates from the external system, your cache hit ratio plummets. In this talk, you will learn how to ensure that your system’s uncached response times are as light as possible when you get a cache miss by minimizing the I/O of your application with intelligent batch data preloading.
3. Cache Pitfalls
● Combination of variables that invalidate it
● Slow updates in the backend
● Cache memory usage grows with the catalog size
● Caching onion nightmares
11. Why does it exist in Magento core?
- Introduction of excessive abstractions
- Promotion of singular service access
- System is designed from the bottom up
12. Can we fix it without modifying core?
- Observe load events on collections of interest
- Load data for related items in the collection
- Create a plugin around a service that triggers
N+1
15. 1. Implement DataLoader interface on your custom service with:
○ isApplicable(string $type) method for deciding if loaded
○ load(ScopeFilter $filter, ProductWrapper[] $products) for loading
related data for a list of product at once
2. Register custom loader as a dependency in di.xml for
LoaderService
3. Use LoadService for accessing data in your plugin with:
○ get(int $productId, string $type) method for accessing data
○ has(int $productId, string $type) method for checking when preload was
done for this product
How to use
17. Target Goal
● Remove load in the loop for the price
retrieval
● Remove load in the loop for the inventory
check
● Achieve below 90 queries on category page with
configurables
● Simple product status is ignored in the
example for simplicity
18. Loader For Simple Product Prices in Configurable
class ConfigurablePrice implements DataLoader
{
public const DATA_KEY = 'configurable_price';
private ResourceConnection $resourceConnection;
public function __construct(ResourceConnection $resourceConnection)
{
$this->resourceConnection = $resourceConnection;
}
public function load(ScopeFilter $filter, array $products): array
{
/*...*/
}
public function isApplicable(string $type): bool
{
return $type === self::TYPE_LIST;
}
}
19. Loader For Simple Product Prices in Configurable
public function load(ScopeFilter $filter, array $products): array
{
$configurableProductIds = [];
foreach ($products as $product) {
if ($product->isType(Configurable::TYPE_CODE)) {
$configurableProductIds[] = $product->getId();
}
}
if (!$configurableProductIds) {
return [];
}
$connection = $this->resourceConnection->getConnection('catalog');
$priceIndexTable = $this->resourceConnection->getTableName('catalog_product_index_price','catalog');
$relationTable = $this->resourceConnection->getTableName('catalog_product_super_link','catalog');
/*...*/
}
26. Key takeaways
- Avoid using cache for data within cached page
- Use single query per set of data
- Disable block_html cache
- Use profiler to find bottlenecks
27. Future plans
- Make it a plug-and-play module
- Create common preloaders of product data
- MSI qty retrieval for shopping cart and checkout
- FPC identities retrieval for composite products
- Create preloader module for categories
- Rewrite setup:di:compile with higher performance
solution
28. QA & Links
Example in the slides:
https://github.com/EcomDev/example-preloading-configurable-product
Preloader Library:
https://github.com/EcomDev/magento2-product-preloader
Contacts:
https://ivanchepurnyi.github.io/