Table Of Contents
- Intro
- The Underlying Problem & Its Roots
- The Classic "Chicken or the Egg" Dilemma
- The Pitfall of CLI Command Constructors
- Strategies to Prevent These Errors
- Simplify with Automated Proxy Injection
- Summary
Intro
Hello everyone,
In this article, I'll provide practical examples showcasing how unexpected challenges can arise in Magento 2 & Adobe Commerce projects while building and/or using custom CLI commands from 3rd parties. This becomes especially significant while working having CI/CD and performing project setups and test execution in pipelines.
Introducing the main "hero" of our article: ISSUE: magento setup command is throwing magento.flag does't exist.. We'll take a deeper look into this issue, understanding its roots and finding ways to address it.
Disclaimer: Just a quick heads-up on naming - both Adobe Commerce and Magento 2 are covered in this article. After all, Adobe Commerce is Magento. Long Live Magento! ๐
The Underlying Problem & Its Roots
While working on various Magento 2 projects, I frequently encountered snags when performing a clean
setup of a project using a freshly created database. This usually happens during integration or functional test setups in pipelines & local environments. Still, this issue isn't exclusive to that scenario and can pop up during upgrades, migrations, or deployments.
The culprits? Here are two pesky errors you might recognize:
The default website isn't defined. Set the website and try again.
For Commerce editions, there's also:
SQLSTATE[42S02]: Base table or view not found: 1146 Table
'magento2.flag' doesn't exist, query was:
SELECT flag.* FROM flag WHERE (flag.flag_code='staging')
When you see these during the php bin/magento setup:install
command execution, they offer little insight into the real issue. After a few hair-pulling hours of research, you'd discover these errors stem from the premature invocation of the DB resource, triggering the initialization of config and state entities from the Magento DB.
But here's the conundrum: You can't fetch data from the DB at this point because there aren't any tables present yet. They only appear and populate during the installation process, not beforehand. ยฏ_(ใ)_/ยฏ
The Classic "Chicken or the Egg" Dilemma
So, we're caught in a circular problem: trying to install the system but getting stopped because it's not installed. Confusing, right? ๐คช
This happens because someone, somewhere, is prematurely calling the DB connection resource. It is obvious, and I hope you would be as grateful as Sam Eliot is for this revelation insight.
This "someone" should be inside bin/magento
and called before actual command execution. Usually, CLI command constructors are guilty here. Let me explain why.
The Pitfall of CLI Command Constructors
For clarity, take a look at this code:
class DoSmthing
{
private ?string $dummyConfigValue;
public function __construct(ScopeConfigInterface $storeManager)
{
$this->dummyConfigValue = $storeManager->getValue('some/dummy/path', ScopeInterface::SCOPE_STORE);
}
public function getValue(): ?string
{
return $this->dummyConfigValue;
}
}
class DummyCommand extends Command
{
...
public function __construct(DoSmthing $model, string $name = null)
{
$this->model = $model;
parent::__construct($name);
}
...
}
The root of the problem lies in the constructors of both the command and model classes.
In the DoSmthing
class, the system attempts to retrieve a config value. This action calls the store
, store_website
, and core_config_data
tables when creating the DummyCommand
object.
And guess what happens when this code runs before the tables are ready? Yep, errors appear!
Lets repeat one more time:
- We execute
php bin/magento setup:install
; -
bin/magento
application creates all instances of CLI commands; -
DummyCommand
instance depends on theDoSmthing
model. SoDoSmthing
should be created beforeDummyCommand
object creation; - Application performs creation of
DoSmthing
model and executesDoSmthing::__construct
method; -
DoSmthing::__construct
method calls to the config manager; - Config manager calls to a chain of tables that are not created yet;
- ERROR
Strategies to Prevent These Errors
Remember this golden rule: Every CLI command class initializes every time you invoke php bin/magento
. So, here's how to save yourself future headaches:
Rule 1 - Be Wise
It is not the most wise solution to call for potential resource consuming operations like DB/API calls in __construct
methods of CLI command classes. Those operations will be constantly executed even once you'd have a wish to get only a list of available commands.
Rule 2 - Be Ready
Steer clear of resources in __construct
that might NOT be available during runtime, especially since Magento tables aren't present during setup:install
.
Rule 3 - Fix The Problem
If you're not in control of the troublesome code, inject a Proxy
using di configs.
We can easily solve our problem from the example with DummyCommand
by just adding those lines in di.xml
:
<type name="AbcTest\DummyConsoleCommand\Console\Command\DummyCommand">
<arguments>
<argument name="model" xsi:type="object">\AbcTest\DummyConsoleCommand\Model\DoSmthing\Proxy</argument>
</arguments>
</type>
Proxy is a well-known pattern, and in Magento, it is widely used to avoid unneeded code execution before we actually request it. In our example - DoSmthing
class object will not be instantiated until we do call for any method of this class.
No config retrieving while CLI class object creation - No error.
More details about proxy here: Proxies .
Simplify with Automated Proxy Injection
For those with a small number of extensions on their project, pinpointing the rogue CLI commands isn't too challenging. However, for more extensive, legacy projects or ones brimming with third-party extensions, identifying the culprits becomes an ordeal.
To save time, we've crafted a solution to automate the proxy injection process. Introducing: run-as-root/magento-cli-auto-proxy.
This package injects Proxy
into all __construct
arguments of CLI commands, eligible for proxying. The result? No errors related to early resource access and, as a side affect, faster execution of php bin/magento
and php bin/magento setup:install
.
Check out the README for more details. We always appreciate feedback and contributions!
Summary
Let's sum it up:
- Unthoughtfully created CLI commands can unexpectedly lead to problems, even if they appear harmless at first.
- The core of the problem often arises from premature database calls, leading to a classic "chicken or the egg" scenario where system installation gets stalled.
- One significant pitfall identified is the CLI command constructors and their potential to wreak havoc when invoked at the wrong time.
- To combat these challenges, we laid out three guiding principles:
- Be Wise by avoiding resource-intensive calls in constructors.
- Be Ready by steering clear of resources that might not be available.
- Fix The Problem by utilizing proxies for problematic third-party code.
- To streamline and automate the solution, we introduced the package run-as-root/magento-cli-auto-proxy which aims to automate proxy injections, offering a more efficient and error-free Magento experience.
Thank you for diving deep into this article. I welcome all your questions and appreciate any feedback you might have.
Happy coding and... Long Live Magento! ๐
Top comments (0)