Lock¶
Prevent multiple processes from doing the same operation on a host, using good old Linux fnctl locking.
fcntl’s flock
is a great choice to prevent deadlocks if/when your process
crashes because the system will release any locks of a process when it dies.
man fcntl
for details.
Anyway, we provide a Lock class that acts as a context manager for both blocking and non-blocking fcntl locks.
It’s especially useful to orchestrate Ansible Action plugins.
Blocking locks¶
Consider this little program:
import cli2
import os
os.environ['LOG_LEVEL'] = 'DEBUG'
with cli2.Lock('/tmp/mylock') as lock:
input('say hello')
Run it in a first terminal: it will log “Acquired” and show the “say hello” input.
Run it in another terminal: it will log “Waiting”
Type enter in the first terminal: it will release the lock and exit
Then the second terminal will log “Acquired” and display the say hello input
You got this: two programs cannot enter the same blocking lock at the same time.
Non blocking locks¶
You’re starting a bunch of processes that potentially want to do the same thing at the same time, ie. download a file to cache locally prior to sending it.
Only one process must do the caching download, the first one that gets the lock, all others will sit there and wait.
This is possible with a non-blocking lock that we later convert into a blocking lock.
with cli2.Lock('/tmp/mylock', blocking=False) as lock:
if lock.acquired:
# we got the lock, proceed to downloading safely
do_download()
else:
# couldn't acquired the lock because another process got it
# let's just wait for that other process to finish by converting
# the non-blocking lock into a blocking one
lock.block()
# all processes can safely process to uploading
do_upload()
- class cli2.lock.Lock(lock_path, blocking=True, prefix=None)[source]¶
fcntl flock context manager, blocking and non blocking modes
In doubt? set
LOG_LEVEL
toDEBUG
and you will see exactly what’s happening.- lock_path¶
Path to the file that we’re going to use with flock.
- blocking¶
If True, the locker automatically blocks. Otherwise, you need to check
acquired
and call :py:method:`block` yourself.
- prefix¶
Arbitrary string that will be added to logs.