Mohammed Joraid
Mohammed Joraid

Reputation: 6480

How to run a script only once at a time and get a list of current running scripts?

Short story:

I have a script called (cron_mailings.php) and I need to know if it is running at the moment. Is there a function or a variable like $_SERVER to get the number of instances of current script (cron_mailings.php)? Because I don't want the script to run more than once at any given moment.

Long story:

We have a cron job that calls a list of scripts, one of the scripts is (cron_mailings.php). cron_mailings.php goes to mailings table, fetches a list of email records -say first 100- then send and delete them from the table. Later the cron job will execute again and callcron_mailings.php that will process the next 100 emails.

The issue is, someone has wrongly set the cron job whereby last week it ran 6 times in 5 seconds and we had a typical Race Condition, the first instance fetched first 100 user, while it was processing user #10 another instance of the same script was called by the cron job, cron_mailings ran and fetched 100 users. Now both scripts are overlapping with 90 users who received the same email twice.

We have already fixed the cron job, but just in case I had to find a solution since i don't know who will be playing with that scrip in the future, therefore

I have implemented two solutions:

  1. Add a field-flag to the db mailings table called is_fetched (1 for fetched, 0 otherwise). And set cron_mailings.php to fetch only records marked with is_fetched =0. By that solution more than once instance of the scrip can actually run and work on different parts of the table .e.g. first instance calles first 100 records and assign 1 to is_fetched, another instance will come and take the second 100 records where is_fetched = 0 and work on them.

  2. Block the whole script by puting a flag into db table to know if the scrip is running, then at the top of the script something like:

    check if (is_cron_maillings_running() === 'Y')
    {
        die("script is running at the moment");
    }
    else
    {
        set_is_cron_maillings_running("Y");
        // do some stuff - send emails and save the world
        //1
        //.
        //.
        //10
        set_is_cron_maillings_running("N");
    }
    

Both solutions are fine (Basically the second one will overpower the first one)

But I have an issue:

What if the script died/crashed/error thrown/db server gone away/ .etc between step 1 and 10 before reaching the last line set_is_cron_maillings_running("N"); ? Any other scrip called by the cron job will see is_cron_maillings_running = Y in the db table and will never execute again, unless someone manually reassign the value to N.

I'm quite open for suggestions regarding finding a better way locking the script. So far in my opinion, i think if i manage to get a list of running scripts (search if current script is running now) from the server it self, that would be safer than assign values to db tables where scrip could not really fully execute and assign is running = N.

Upvotes: 0

Views: 443

Answers (1)

leftclickben
leftclickben

Reputation: 4614

Instead of storing Y/N in the lock file/field, store the date when the lock is set (Y), or null for no lock (N). Then if that date is more than X amount of time ago, ignore it. This way if something goes wrong, your script will start working again once the lock is stale.

Upvotes: 1

Related Questions