Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP locking / making sure a given script is only running once at any given time

Tags:

php

locking

I'm trying to write a PHP script that I want to ensure only has a single instance of it running at any given time. All of this talk about different ways of locking, and race conditions, and etc. etc. etc. is giving me the willies.

I'm confused as to whether lock files are the way to go, or semaphores, or using MySQL locks, or etc. etc. etc.

Can anyone tell me:

a) What is the correct way to implement this?

AND

b) Point me to a PHP implementation (or something easy to port to PHP?)

like image 332
Keith Palmer Jr. Avatar asked Jan 31 '12 16:01

Keith Palmer Jr.


People also ask

What is file locking in PHP?

File locking is an important task. It restricts other users from changing a specific file. It allows only one user at a time to access or modify it. PHP provides the flock() method for portable advisory file locking. This was introduced with PHP version 4.0.

Which of the following PHP function is used to make a user locked out from a website?

php") . ". lock"); if (! tryLock()) die("Already running.

What is the use of flock function?

flock() allows you to perform a simple reader/writer model which can be used on virtually every platform (including most Unix derivatives and even Windows). The lock is released also by fclose(), or when stream is garbage collected.

How do you lock a file in shell script?

Using lockfile as the condition of a loop in a shell script can be done easily by using the -! flag to invert the exit status. To prevent infinite loops, failures for any reason other than the lockfile already existing are not inverted to success but rather are still returned as failures.


2 Answers

One way is to use the php function flock with a dummy file, that will act as a watchdog.

On the beginning of our job, if the file raise a LOCK_EX flag, exit, or wait, can be done.

Php flock documentation: http://php.net/manual/en/function.flock.php

For this examples, a file called lock.txt must be created first.

Example 1, if another twin process is running, it will properly quit, without retrying, giving a state message.

It will throw the error state, if the file lock.txt isn't reachable.

<?php

$fp = fopen("lock.txt", "r+");

if (!flock($fp, LOCK_EX|LOCK_NB, $blocked)) {
    if ($blocked) {

        // another process holds the lock
        echo "Couldn't get the lock! Other script in run!\n"; 

    }
    else {
        // couldn't lock for another reason, e.g. no such file
        echo "Error! Nothing done.";
    }
}
else {
    // lock obtained
    ftruncate($fp, 0);  // truncate file

    // Your job here 
    echo "Job running!\n";
    
    // Leave a breathe
    sleep(3);

    fflush($fp);            // flush output before releasing the lock
    flock($fp, LOCK_UN);    // release the lock

}

fclose($fp); // Empty memory

Example 2, FIFO (First in, first out): we wants the process to wait, for an execution after the queue, if any:

<?php

$fp = fopen("lock.txt", "r+");

if (flock($fp, LOCK_EX)) {  // acquire an exclusive lock
    ftruncate($fp, 0);      // truncate file

    // Your job here 
    echo "Job running!\n";

    // Leave a breathe
    sleep(3);

    fflush($fp);            // flush output before releasing the lock
    flock($fp, LOCK_UN);    // release the lock
}

fclose($fp);

It is also doable with fopen into x mode, by creating and erasing a file when the script ends.

Create and open for writing only; place the file pointer at the beginning of the file. If the file already exists, the fopen() call will fail by returning FALSE

http://php.net/manual/en/function.fopen.php


However, into a Unix environment, for fine tuning, I found easier to list the PID's of every background scripts with getmypid() into a DB, or a separate JSON file.

When one task ends, the script is responsible to declare his state in this file (eq: success/failure/debug infos, etc), and then remove his PID. This allows from my view to create admins tools and daemons in a simpler way. And use posix_kill() to kill a PID from PHP if necessary.

Micro-Services are composed using Unix-like pipelines. Services can call services. https://en.wikipedia.org/wiki/Microservices


See also: Prevent PHP script using up all resources while it runs?

like image 160
NVRM Avatar answered Sep 25 '22 00:09

NVRM


Use semaphores:

$key = 156478953; //this should be unique for each script
$maxAcquire = 1;
$permissions =0666;
$autoRelease = 1; //releases semaphore when request is shut down (you dont have to worry about die(), exit() or return
$non_blocking = false; //if true, fails instantly if semaphore is not free

$semaphore = sem_get($key, $maxAcquire, $permissions, $autoRelease);
if (sem_acquire($semaphore, $non_blocking ))  //blocking (prevent simultaneous multiple executions)
{
    processLongCalculation();
}
sem_release($semaphore);

See:

https://www.php.net/manual/en/function.sem-get.php

https://www.php.net/manual/en/function.sem-acquire.php

https://www.php.net/manual/en/function.sem-release.php

like image 42
Bojan Hrnkas Avatar answered Sep 23 '22 00:09

Bojan Hrnkas