Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python - ensure script is activated only once

I'm writing a Python 2.7 script.
In summary, this script is being run every night on Linux and activates several processes.

I'd like to ensure this script is not run multiple times in parallel (basically trying to mimic Singleton pattern but on application level) .

Code Example

def main():
    # before doing anything, I'd like to know whether this
    # script was activated and alive. 
    # if so, error out

    # do something

if __name__ == "__main__":
    main()

Suggestion

The naive solution would be to create some kind of a lock file, that acts as a mutex.
The first thing we do is to check whether this file exists. if so, then other instance of the script already created it and we should error out. when the script is done, we remove this file.
I'm assuming this solution would work, as long as the operations on the file system are atomic.

Implementation

import os, sys

lock_file_path = ".lock_script"

def lock_mutex():
    if os.path.exists(lock_mutex_path):
        print "Error: script was already activated."
        sys.exit(-1)

    else:
        file = open(lock_mutex_path, 'w')

def unlock_mutex():
    assert( os.path.exists(lock_mutex_path))
    os.remove(lock_mutex_path)

def main():

    try:
        lock_mutex()

        # do something

        unlock_mutex()

    except:
        unlock_mutex()

if __name__ == "__main__":
    main()

Problem

How to ensure lock_mutex() and unlock_mutex() are atomic?

like image 980
idanshmu Avatar asked Nov 03 '13 10:11

idanshmu


People also ask

How do I run a Python code only once?

To run a function only once:Set a has_run attribute on the function to True the first time it runs. Each time the function is called, check if the has_run attribute is True and return straight away. The code in the function will only run the first time.

How do I temporarily disable a Python script?

Ctrl + C on Windows can be used to terminate Python scripts and Ctrl + Z on Unix will suspend (freeze) the execution of Python scripts. If you press CTRL + C while a script is running in the console, the script ends and raises an exception.

How do I make a Python script run periodically?

Method 1: Using Time Module We can create a Python script that will be executed at every particular time. We will pass the given interval in the time. sleep() function and make while loop is true. The function will sleep for the given time interval.

How do I make something every second in Python?

How do I run something every second in Python? To Execute a function every x seconds in python: Use the time. sleep() method.

How do I run a Python script every morning?

Select 'Daily' and click Next. Now you need to specify at what time your Python Script should be executed daily and then Click on Next. Step 4: In the next step, you need to select the 'Start a Program' option from the given options and then click Next.


2 Answers

Since you're using linux, you can make use of flock:

import os
import fcntl
import time

def main():
  # acquire the prog lock
  if not prog_lock_acq('singleton.lock'):
    print("another instance is running")
    exit(1)

  print("program is running-press Ctrl+C to stop")
  while True:
    time.sleep(10)

def prog_lock_acq(lpath):
  fd = None
  try:
    fd = os.open(lpath, os.O_CREAT)
    fcntl.flock(fd, fcntl.LOCK_NB | fcntl.LOCK_EX)
    return True
  except (OSError, IOError):
    if fd: os.close(fd)
    return False

if __name__ == '__main__':
  main()

It doesn't matter that we left the file open after exiting the prog_lock_acq because when the process exits, it will be automatically closed by the OS. Also, if you leave out LOCK_NB option, the flock call will block until the current running process quits. Depending on your use case, that might be useful.

Note that we're not deleting the file on exit. It doesn't matter. Existence of the file doesn't indicate a live process—the lock does. So even if you kill your process with kill -9, the lock is still released.

There is however a caveat: if you unlink the lock file while the process is running, when the next instance of the process is run, it will create a new file which will have no lock on it and will run just fine which will violate our singleton design. You might be able to do something clever with a directory to prevent unlinking but I'm not sure how robust that would be.

like image 85
Mansour Avatar answered Oct 18 '22 21:10

Mansour


I use supervisor (http://supervisord.org/) to run stuff under Linux. It runs Django, Celeryd and so on and ensures that they get restarted in case they finish unexpectedly.

But it's also possible to set the options so commands aren't started or restarted automatically when it finishes: autostart=false, autorestart=false, starseconds=0. I use that for these cron jobs.

In cron I put the command "supervisorctl start myscript", which does nothing if myscript is already running under supervisor, and otherwise starts it.

Works perfectly, regardless of the language that the script is written in.

like image 40
RemcoGerlich Avatar answered Oct 18 '22 21:10

RemcoGerlich