From 7b9b621af2d17d6a357e5a848e78e51da6714121 Mon Sep 17 00:00:00 2001 From: Paul Hachmang Date: Thu, 24 Sep 2026 23:15:50 +0200 Subject: [PATCH 1/2] The stock id of the current website comes from the cache FastBootInventory answers GetStockIdForCurrentWebsite from a local entry per website code, under the stock_id switch. A write of a stock's sales channel links cleans the entries. --- CHANGELOG.md | 4 + README.md | 1 + composer.json | 6 +- dev/README.md | 1 + src/FastBootCache/Model/Feature.php | 2 +- src/FastBootCache/Model/Tag.php | 1 + .../Plugin/ForgetStockIds.php | 31 ++++++++ .../Plugin/StockIdFromCache.php | 48 +++++++++++ src/FastBootInventory/README.md | 10 +++ .../Test/Unit/Plugin/StockIdFromCacheTest.php | 79 +++++++++++++++++++ src/FastBootInventory/composer.json | 22 ++++++ src/FastBootInventory/etc/di.xml | 27 +++++++ src/FastBootInventory/etc/module.xml | 10 +++ src/FastBootInventory/registration.php | 6 ++ 14 files changed, 245 insertions(+), 3 deletions(-) create mode 100644 src/FastBootInventory/Plugin/ForgetStockIds.php create mode 100644 src/FastBootInventory/Plugin/StockIdFromCache.php create mode 100644 src/FastBootInventory/README.md create mode 100644 src/FastBootInventory/Test/Unit/Plugin/StockIdFromCacheTest.php create mode 100644 src/FastBootInventory/composer.json create mode 100644 src/FastBootInventory/etc/di.xml create mode 100644 src/FastBootInventory/etc/module.xml create mode 100644 src/FastBootInventory/registration.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 0279967..a150d45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +- The stock id of the current website as a cache entry in the local files, from the new FastBootInventory module: no sales channel select per request. A change of a stock's sales channels cleans it. + ## 0.2.0-rc8 - Tax rates, customer groups and currency rates as cache entries in the local files: no select per request for them. A tax rule, rate, class or group save cleans the tax entries; a rate import cleans the currency entries. diff --git a/README.md b/README.md index ee89a68..8871250 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ composer require graphcommerce/magento-fast-boot | FastBoot | [Installation and deployment](src/FastBoot/README.md) | | FastBootCache | [Cache configuration and behavior](src/FastBootCache/README.md) | | FastBootGraphQl | [GraphQL optimizations](src/FastBootGraphQl/README.md) | +| FastBootInventory | [The stock id of a website from the cache](src/FastBootInventory/README.md) | | FastBootPreload | [Class preloading](src/FastBootPreload/README.md) | ## Online benchmarks diff --git a/composer.json b/composer.json index 48e1dd7..099a958 100644 --- a/composer.json +++ b/composer.json @@ -32,13 +32,15 @@ "src/FastBootCache/registration.php", "src/FastBoot/registration.php", "src/FastBootPreload/registration.php", - "src/FastBootGraphQl/registration.php" + "src/FastBootGraphQl/registration.php", + "src/FastBootInventory/registration.php" ], "psr-4": { "GraphCommerce\\FastBootCache\\": "src/FastBootCache/", "GraphCommerce\\FastBoot\\": "src/FastBoot/", "GraphCommerce\\FastBootPreload\\": "src/FastBootPreload/", - "GraphCommerce\\FastBootGraphQl\\": "src/FastBootGraphQl/" + "GraphCommerce\\FastBootGraphQl\\": "src/FastBootGraphQl/", + "GraphCommerce\\FastBootInventory\\": "src/FastBootInventory/" } }, "archive": { diff --git a/dev/README.md b/dev/README.md index 0a097f1..4dd92de 100644 --- a/dev/README.md +++ b/dev/README.md @@ -30,6 +30,7 @@ Production installs use the default-enabled optimizations. These switches suppor | `tax_rates` | Tax rates and applied rates per rate request from the cache, for requests that are not customer-specific. | Yes | | `customer_groups` | Customer groups by id from the cache, with their excluded websites. | Yes | | `currency_rates` | Currency rates from the cache; a rate import cleans them. | Yes | +| `stock_id` | The stock id of the current website from the cache; a change of a stock's sales channels cleans it. | Yes | These switches do not enable PHP class preload; that requires `opcache.preload` at FPM startup. diff --git a/src/FastBootCache/Model/Feature.php b/src/FastBootCache/Model/Feature.php index 486dc80..abcf41b 100644 --- a/src/FastBootCache/Model/Feature.php +++ b/src/FastBootCache/Model/Feature.php @@ -15,7 +15,7 @@ class Feature { private ?array $switches = null; - private const CACHED = ['schema_array' => true,'cache_files' => true,'system_config_array' => true,'view_config' => true,'parsed_queries' => true,'validated_queries' => true,'scopes_cache' => true,'website_stores' => true,'deploy_config_unchanged' => true,'placeholder_url' => true,'guest_tax_factor' => true,'tax_rates' => true,'customer_groups' => true,'currency_rates' => true]; + private const CACHED = ['schema_array' => true,'cache_files' => true,'system_config_array' => true,'view_config' => true,'parsed_queries' => true,'validated_queries' => true,'scopes_cache' => true,'website_stores' => true,'deploy_config_unchanged' => true,'placeholder_url' => true,'guest_tax_factor' => true,'tax_rates' => true,'customer_groups' => true,'currency_rates' => true,'stock_id' => true]; public function __construct( private readonly DeploymentConfig $deploymentConfig, diff --git a/src/FastBootCache/Model/Tag.php b/src/FastBootCache/Model/Tag.php index ebcb35a..889d7cb 100644 --- a/src/FastBootCache/Model/Tag.php +++ b/src/FastBootCache/Model/Tag.php @@ -11,4 +11,5 @@ final class Tag { public const TAX = 'FASTBOOT_TAX'; public const CURRENCY = 'FASTBOOT_CURRENCY'; + public const STOCK = 'FASTBOOT_STOCK'; } diff --git a/src/FastBootInventory/Plugin/ForgetStockIds.php b/src/FastBootInventory/Plugin/ForgetStockIds.php new file mode 100644 index 0000000..92b7199 --- /dev/null +++ b/src/FastBootInventory/Plugin/ForgetStockIds.php @@ -0,0 +1,31 @@ +cache->clean([Tag::STOCK]); + + return $result; + } +} diff --git a/src/FastBootInventory/Plugin/StockIdFromCache.php b/src/FastBootInventory/Plugin/StockIdFromCache.php new file mode 100644 index 0000000..086b8f2 --- /dev/null +++ b/src/FastBootInventory/Plugin/StockIdFromCache.php @@ -0,0 +1,48 @@ +feature->on(self::SWITCH)) { + return $proceed(); + } + $store = $this->storeManager->getStore($this->request->getParam('store')); + $key = self::KEY . $this->storeManager->getWebsite($store->getWebsiteId())->getCode(); + $cached = $this->cache->load($key); + $entry = is_string($cached) ? json_decode($cached, true) : null; + if (is_array($entry) && isset($entry['id'])) { + return (int)$entry['id']; + } + $stockId = $proceed(); + $this->cache->save(json_encode(['id' => $stockId]), $key, [ConfigCache::CACHE_TAG, Tag::STOCK]); + + return $stockId; + } +} diff --git a/src/FastBootInventory/README.md b/src/FastBootInventory/README.md new file mode 100644 index 0000000..9819ada --- /dev/null +++ b/src/FastBootInventory/README.md @@ -0,0 +1,10 @@ +# GraphCommerce_FastBootInventory + +The stock id of the current website from the cache. Core resolves it with two selects on every +request that loads a product collection. + +[`Plugin/StockIdFromCache`](Plugin/StockIdFromCache.php) answers +`Magento\InventoryCatalog\Model\GetStockIdForCurrentWebsite::execute()` from the entry +`FASTBOOT_STOCK_ID_`, under the `stock_id` switch. +[`Plugin/ForgetStockIds`](Plugin/ForgetStockIds.php) cleans the entries after a write of a +stock's sales channel links. diff --git a/src/FastBootInventory/Test/Unit/Plugin/StockIdFromCacheTest.php b/src/FastBootInventory/Test/Unit/Plugin/StockIdFromCacheTest.php new file mode 100644 index 0000000..1e3f2ab --- /dev/null +++ b/src/FastBootInventory/Test/Unit/Plugin/StockIdFromCacheTest.php @@ -0,0 +1,79 @@ +cache = $this->createMock(CacheInterface::class); + $this->cache->method('load')->willReturnCallback(fn(string $id) => $this->entries[$id] ?? false); + $this->cache->method('save')->willReturnCallback(function (string $data, string $id) { + $this->entries[$id] = $data; + + return true; + }); + $this->cache->method('clean')->willReturnCallback(function (array $tags) { + $this->entries = in_array(Tag::STOCK, $tags, true) ? [] : $this->entries; + + return true; + }); + + return $this->cache; + } + + private function plugin(): StockIdFromCache + { + $feature = $this->createStub(Feature::class); + $feature->method('on')->willReturn(true); + + $store = $this->createStub(StoreInterface::class); + $store->method('getWebsiteId')->willReturn(1); + $website = $this->createStub(WebsiteInterface::class); + $website->method('getCode')->willReturn('base'); + $storeManager = $this->createStub(StoreManagerInterface::class); + $storeManager->method('getStore')->willReturn($store); + $storeManager->method('getWebsite')->willReturn($website); + + return new StockIdFromCache($this->cache(), $feature, $storeManager, $this->createStub(RequestInterface::class)); + } + + public function testTheStockIdIsAnEntryUntilTheSalesChannelsChange(): void + { + $plugin = $this->plugin(); + $subject = $this->createStub(GetStockIdForCurrentWebsite::class); + $calls = 0; + $proceed = function () use (&$calls): int { + $calls++; + + return 3; + }; + + $this->assertSame(3, $plugin->aroundExecute($subject, $proceed)); + $this->assertSame(3, $plugin->aroundExecute($subject, $proceed)); + $this->assertSame(1, $calls); + $this->assertSame('{"id":3}', $this->entries['FASTBOOT_STOCK_ID_base']); + + (new ForgetStockIds($this->cache))->afterExecute($this->createStub(ReplaceSalesChannelsDataForStock::class), null); + $plugin->aroundExecute($subject, $proceed); + $this->assertSame(2, $calls); + } +} diff --git a/src/FastBootInventory/composer.json b/src/FastBootInventory/composer.json new file mode 100644 index 0000000..261143c --- /dev/null +++ b/src/FastBootInventory/composer.json @@ -0,0 +1,22 @@ +{ + "name": "graphcommerce/module-fast-boot-inventory", + "description": "The stock of a website from the cache instead of the sales channel selects per request", + "type": "magento2-module", + "license": "MIT", + "require": { + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "graphcommerce/module-fast-boot-cache": "*", + "magento/framework": "^103.0.8", + "magento/module-inventory-catalog": "*", + "magento/module-inventory-sales": "*", + "magento/module-store": "^101.1.8" + }, + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "GraphCommerce\\FastBootInventory\\": "" + } + } +} diff --git a/src/FastBootInventory/etc/di.xml b/src/FastBootInventory/etc/di.xml new file mode 100644 index 0000000..4e04547 --- /dev/null +++ b/src/FastBootInventory/etc/di.xml @@ -0,0 +1,27 @@ + + + + + + FASTBOOT_STOCK_ID_ + + + GraphCommerce\FastBootCache\Model\Tag::STOCK + + + + + + + + + + + + + + + + + + diff --git a/src/FastBootInventory/etc/module.xml b/src/FastBootInventory/etc/module.xml new file mode 100644 index 0000000..4016fc9 --- /dev/null +++ b/src/FastBootInventory/etc/module.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/FastBootInventory/registration.php b/src/FastBootInventory/registration.php new file mode 100644 index 0000000..922bdb2 --- /dev/null +++ b/src/FastBootInventory/registration.php @@ -0,0 +1,6 @@ + Date: Thu, 24 Sep 2026 23:32:48 +0200 Subject: [PATCH 2/2] The stock id test runs without the inventory modules --- .../Test/Unit/Plugin/StockIdFromCacheTest.php | 2 ++ .../Test/Unit/_files/inventory-classes.php | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 src/FastBootInventory/Test/Unit/_files/inventory-classes.php diff --git a/src/FastBootInventory/Test/Unit/Plugin/StockIdFromCacheTest.php b/src/FastBootInventory/Test/Unit/Plugin/StockIdFromCacheTest.php index 1e3f2ab..9542dd4 100644 --- a/src/FastBootInventory/Test/Unit/Plugin/StockIdFromCacheTest.php +++ b/src/FastBootInventory/Test/Unit/Plugin/StockIdFromCacheTest.php @@ -17,6 +17,8 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +require_once __DIR__ . '/../_files/inventory-classes.php'; + class StockIdFromCacheTest extends TestCase { private array $entries = []; diff --git a/src/FastBootInventory/Test/Unit/_files/inventory-classes.php b/src/FastBootInventory/Test/Unit/_files/inventory-classes.php new file mode 100644 index 0000000..08733c5 --- /dev/null +++ b/src/FastBootInventory/Test/Unit/_files/inventory-classes.php @@ -0,0 +1,28 @@ +