Lock your own processes in Magento
Inevitably, when you get into the weeds with a Magento build, you’ll need to run some big, hairy process — perhaps even on a regular basis. Perhaps it involves processing images, or updating data, or creating reports. Perhaps it runs on cron, but you’d also like to run it from the command line. And if it’s destructive — i.e., it alters data — you definitely want to make sure you’re only running one at a time.
Magento has a nice semaphore locking process handler built in to its indexers. In order for a reindexing process to run, the process needs to obtain a lock. The code lives in the Mage_Index_Model_Process class, and has helpful methods such as isLocked(), lockAndBlock(), and unlock(). These look for and manipulate files in your application’s var/locks directory.
The implementation is a tried and true locking mechanism that you could write yourself, but why bloat an already massive code base? We can just repurpose this relatively self-contained functionality whenever we need to lock a process.
In var/locks, you should see a lock file for every index process, conveniently named 1-9:
$ ls -l var/locks total 40 -rw-rw-rw- 1 apache magento 31 Aug 29 07:26 index_process_1.lock -rw-rw-rw- 1 apache magento 31 Aug 29 10:41 index_process_2.lock -rw-rw-rw- 1 apache magento 31 Aug 29 07:26 index_process_3.lock -rw-rw-rw- 1 apache magento 31 Aug 29 08:19 index_process_4.lock -rw-rw-rw- 1 apache magento 31 Aug 29 08:20 index_process_5.lock -rw-rw-rw- 1 apache magento 31 Aug 29 08:20 index_process_6.lock -rw-rw-rw- 1 apache magento 31 Aug 29 02:52 index_process_7.lock -rw-rw-rw- 1 apache magento 31 Aug 29 08:54 index_process_8.lock -rw-rw-rw- 1 apache magento 31 Aug 29 02:55 index_process_9.lock
Mage_Index_Model_Process knows which lock file to set for which process via its own Id property. The rest of the name (“index_process_” and “.lock”) are hard-coded into the implementation.
To take advantage of this, we first need some code that does something.
<?php
class BigThings
{
/**
* Process all the things
*/
public function changeEverything()
{
$this->log("Starting...hold on to your hats.");
for ($i = 1; $i < 10000000000; $i++)
{
// Do something complicated with $i.
}
$this->log("Finished! Whew.");
}
/**
* Log a message to stdout.
*
* @param $msg
*/
public function log($msg)
{
echo $msg . PHP_EOL;
}
}
Super. So, let’s assume that this might run from cron, that multiple users might trigger the process at once, etc. The world is complicated. We need a copy of the core class to work with, and our own process ID to feed to the class.
<?php
class BigThings
{
/**
* Our process ID.
*/
const PROCESS_ID = 'bigthings';
/**
* Mage_Index_Model_Process will provide us a lock file API.
*
* @var Mage_Index_Model_Process $indexProcess
*/
private $indexProcess;
/**
* Constructor. Instantiate the Process model, and set our custom
* batch process ID.
*/
public function __construct()
{
$this->indexProcess = new Mage_Index_Model_Process();
$this->indexProcess->setId(self::PROCESS_ID);
}
Next, we’ll try to obtain a lock at the beginning of our heavy method. The isLocked() method will return true if another process has the lock, false otherwise.
/**
* Process all the things!
*/
public function changeEverything()
{
if ($this->indexProcess->isLocked())
{
$this->log(sprintf("Another %s process is running! Abort", self::PROCESS_ID));
return false;
}
// Set an exclusive lock.
$this->indexProcess->lockAndBlock();
$this->log("Starting...hold on to your hats.");
for ($i = 1; $i < 10000000000; $i++)
{
// Do something complicated with $i.
}
$this->log("Finished! Whew.");
// Remove the lock.
$this->indexProcess->unlock();
return true;
}
After the first run, you’ll see a new entry in var/locks:
$ ls -l var/locks total 40 -rw-rw-rw- 1 apache magento 31 Aug 29 07:26 index_process_1.lock -rw-rw-rw- 1 apache magento 31 Aug 29 10:41 index_process_2.lock -rw-rw-rw- 1 apache magento 31 Aug 29 07:26 index_process_3.lock -rw-rw-rw- 1 apache magento 31 Aug 29 08:19 index_process_4.lock -rw-rw-rw- 1 apache magento 31 Aug 29 08:20 index_process_5.lock -rw-rw-rw- 1 apache magento 31 Aug 29 08:20 index_process_6.lock -rw-rw-rw- 1 apache magento 31 Aug 29 02:52 index_process_7.lock -rw-rw-rw- 1 apache magento 31 Aug 29 08:54 index_process_8.lock -rw-rw-rw- 1 apache magento 31 Aug 29 02:55 index_process_9.lock -rw-r--r-- 1 magento magento 31 Aug 29 12:45 index_process_bigthings.lock
That’s it! No more worries. The full file is available here.