To me, the concept of Blue Green deployment was familiar because it is part of Shopware. So I was pleasantly surprised to hear that Magento 2.4.4 also started to include this concept. And then it got quiet. What is it and is it useful?
The concept is not new
I first got to know about Blue Green deployment through Martin Fowler. On Wikipedia, it mentions about that this concept assumes two environments: Blue and Green, for instance a production environment and a staging environment. And the deployment allows for swapping these environments without downtime. In essence, this story is all about zero downtime deployments.
If you are doing deployment with Magento properly now, then you are actually already using Blue/Green deployment through DeployPHP, Capistrano, Jenkins, Kubernetes pod replacements or something similar. Blue is the live environment, Green is a shadow copy. You manipulate all files (git pull
, composer install
, static content deployment, DI compilation) in the Green environment. Then you swap the two and do the last bits in the Blue environment (database upgrades). And if there are no database upgrades, this leads to the holy grail: Zero Downtime Deployment with literally 0.00 seconds of downtime.
The problem of Magento database upgrades
The problem lies in that part: Imagine that a new entity is created on the database level. Then to make use of the code (a ViewModel fetches data from a repository, a repository calls upon a resource model, a resource model fetches from a database table), both database structure and codebase need to be in sync. You will need to go down temporarily to create this database table, because the code assumes this table to be there already.
However, you could also make your code smarter, by intercepting an exception when the table is not there yet. And with that, you could prevent downtime: You simply go live with the new code and the old database. And then after the deployment has been rounded up, you create the relevant table (well, as part of the deployment of course).
Unfortunately, Magento does not support this scenario. Until now, with Magento 2.4.4.
What happens if you have database upgrades waiting?
If you deploy new code that tells Magento that there are new database upgrades waiting to be run (via updated db_schema.xml
files, patches or an updated setup_version
), you will be notified that the setup:upgrade
needs to be run. But what happens if you don't?
Well, you bump into a friendly exception when visiting the Magento pages in either backend or frontend. This is sometimes annoying when you activate a new module that has a setup_version
but no actual setup classes. And ofcourse, during deployment, we run setup:upgrade
anyway. But what if that command is actually not needed?
The Blue/Green deployment flag of Magento
In Magento 2.4.4, a new configuration value deployment/blue_green/enabled
has been added. There are two classes using this value: Magento\Deploy\Model\Plugin\ConfigChangeDetector
and Magento\Framework\Module\Plugin\DbStatusValidator
- both DI plugins upon the Magento core class Magento\Framework\App\FrontController
(aka, the routing mechanism).
If the flag is disabled (0
) and the system has detected that changes are awaiting, then the Magento\Deploy\Model\Plugin\ConfigChangeDetector
class throws an exception. So setting the flag to be enabled, simply removes the exception. The second plugin Magento\Framework\Module\Plugin\DbStatusValidator
does the same but seems to focus mainly on the setup_version
flag and not the deployment configuration that you might have dumped in your own env.php
and config.php
.
The main point is: The flag disables the exception.
When to set the flag?
You can enable the flag in your app/etc/env.php
file:
'deployment' => [
'blue_green' => [
'enabled' => true
]
]
Which is nice. Is it? No, it isn't. Because the real conundrum is at what moment you would set this flag. The problem is that Magento does not differentiate between database upgrades that the code is depending upon and database upgrades that the code is not depending upon.
In other words, if you are enabling this flag in production without thinking things over, you or your customers might encounter Fatal Exceptions. On the other hand, it might also be that the code is just working in most circumstances. So, start testing for this.
Start testing!
This is where my story stops at this moment. In my personal playground environment, I have added the flag to be enabled: I use the playground for extension deployment, research and playing around with Magento code. And I have done a bit of testing on my extensions: If the module.xml
definition contains a setup_version
, it either does not mean anything (because there is no setup included), or it means that actual Setup
classes will be run with setup:upgrade
. Elsewhere in the code, you will need to make sure that the code does not crash if the database structure is not there yet.
When a resource model (child of Magento\Framework\Model\ResourceModel\Db\AbstractDb
) or a collection (child of \Yireo\Example\Model\ResourceModel\ExampleItem\Collection
) calls a non-existing table the exception Magento\Framework\DB\Adapter\TableNotFoundException
is thrown. If you simply catch this exception, all will be fine:
public function getItems()
{
try {
$items = $this->collection->getItems();
} catch (TableNotFoundException $tableNotFoundException) {
return [];
}
return $items;
}
Perhaps you are using some non-Magento-DAL-like code, for instance direct queries. In this case, you will need to check for the existence of the table manually: $setup->tableExists($tableName)
.
Once the table is there, you can call upon its columns. If one of the columns is not there yet, the result is usually null
. In general, you don't get an exception like with the non-existing table. All good here.
Be dynamic with the flag
Because of this, you might say: Hey, let's make all the code compatible with this. It only requires intercepting a TableNotFoundException
. Perhaps, but further testing is needed for this. And until the moment that you have guaranteed that all the code works for any future upgrade still to come, perhaps it is better to keep the flag disabled by default:
'deployment' => [
'blue_green' => [
'enabled' => false
]
]
However, this opens up for yet another opportunity: What if the flag is disabled by default, but enabled when you need it. Imagine the following: Let's say you have got a million-record table to which you want to add a new column. The normal Magento way is to deploy the code and the database column at the same time, causing the maintenace mode to be enabled while waiting for the database operation to finish. If this is really the only change, you could temporarily enable the flag, conscientiously knowing that the code that you have will work in production without the database structure being up-to-date. And next, you log in manually to run setup:upgrade
(which potentially takes hours) without downtime!
Further stuff?
I personally think this new Blue Green deployment flag could be gold, but we need to elaborate more to make this work. Yet again, I'm really surprised that no-one else blogged about this yet. Got ideas? If you are coming over to the MageUnconference Netherlands (July 13th-14th, 2023) and we'll have a beer or two over it!
About the author
Jisse Reitsma is the founder of Yireo, extension developer, developer trainer and 3x Magento Master. His passion is for technology and open source. And he loves talking as well.