New in Symfony 2.6: LockHandler

Warning: This post is about an unsupported Symfony version. Some of this information may be out of date. Read the most recent Symfony Docs.
Contributed by
Grégoire Pineau
in #10475.
File locking is a mechanism that restricts access to a computer file by allowing only one user or process access at any specific time. This mechanism was introduced back in 1963 for the mainframes and it will make its debut in Symfony starting from version 2.6.
The new LockHandler
class provides a simple abstraction to lock anything by
means of a file lock. Its most common use case is to avoid race conditions by
locking commands, so the same command cannot be executed concurrently by
different processes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\LockHandler;
class UpdateContentsCommand extends Command
{
protected function configure()
{
// ...
}
protected function execute(InputInterface $input, OutputInterface $output)
{
// create the lock
$lock = new LockHandler('update:contents');
if (!$lock->lock()) {
$output->writeln('The command is already running in another process.');
return 0;
}
// ... do some task
// (optional) release the lock (otherwise, PHP will do it
// for you automatically)
$lock->release();
}
}
The LockHandler
constructor takes as its first argument the lock identifier,
which will be used as part of the name of the file used to create the lock. By
default, locks are created in the temporary directory of the system. If you want
to use a specific directory, pass it as the second optional argument of the
constructor.
The lock()
method returns true
if the lock was acquired and false
otherwise. In addition, you can optionally pass a boolean argument to indicate
if you want to wait until the requested lock is released. This is useful to
execute a command just after the other locking command is finished:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
class UpdateContentsCommand extends Command
{
// ...
protected function execute(InputInterface $input, OutputInterface $output)
{
// create the lock
$lock = new LockHandler('update:contents');
// wait for the lock release as long as necessary
if (!$lock->lock(true)) {
$output->writeln('The command is already running in another process.');
return 0;
}
// ...
}
}
The lock handler has been limited on purpose to work only with file-based locks, because it's extremely complex to make it work on network or databases. This means that it only works when using one and only one host. If you have several hosts, you must not use this helper.
Help the Symfony project!
As with any Open-Source project, contributing code or documentation is the most common way to help, but we also have a wide range of sponsoring opportunities.
Comments
Comments are closed.
To ensure that comments stay relevant, they are closed for old posts.
Could you add the `use` statement for the LockHandler class in your example? Right now we can't see the namespace of this class ;)
I don't have a need for local filesystem locking, but multiple servers via database or virtual filesystem would be quite handy.
Wouldnt it be a good idea if the lock got released after a certain time?
$lock = new LockHandler('update:contents', 3600);
// The lock will auto release after 3600 sec.
You could e.g. use memcached or redis to do atomic locks.
Would you consider adding it?
Any reason why this was implemented directly as a concrete class rather than an interface? Thinking about the mentioned, currently unsupported use-cases of locking using something other than a file.
https://github.com/symfony/symfony/pull/10475