Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python : Locking text file on NFS

I have a file results.txt on a server which is accessed by multiple VMs through NFS. A process runs on each of these VMs which reads the results.txt file and modifies it. If two processes, A and B, read the file at the same time, then modification of either A or B would be present in results.txt based on the order in which the processes write to the file.

If process A has a write lock over the file then process B would have to wait till the lock is released to read the results.txt file.

I have tried implementing this using Python:

import fcntl


f = open("/path/result.txt")
fcntl.flock(f,fcntl.LOCK_EX)
#code

It works as expected for files on the local disk.

but when I run try to lock a file on the mounted path, I get the following error:

Traceback (most recent call last):
  File "lock.py", line 12, in <module>
    fcntl.flock(f,fcntl.LOCK_EX)
IOError: [Errno 45] Operation not supported 

I tried fcntl.fcntl and fcntl.flock but got the same error. Is this an issue with the way I am using fcntl? Is any configuration required on the server where file is stored?

Edit:

This is how I am using fcntl.fcntl:

f= open("results.txt")
lockdata = struct.pack('hhllhh', fcntl.F_RDLCK,0,0,0,0,0)
rv = fcntl.fcntl(f, fcntl.F_SETLKW, lockdata)

The NFS server version is 3.

like image 998
gaganso Avatar asked Jun 04 '16 19:06

gaganso


People also ask

Does NFS support file locking?

The NFS (Versions 2 and 3) protocol does not support file locking, but the NFS environment supports an ancillary protocol called NLM, which originally stood for "Network Lock Manager." When an NFS filesystem on an NFS client gets a request to lock a file, instead of an NFS remote procedure call, it generates an NLM ...

How do you lock a file in Python?

I can use it in the following fashion: from filelock import FileLock with FileLock("myfile. txt"): # work with the file as it is now locked print("Lock acquired.")

What is lockfile for Python?

The lockfile package provides a platform-independent advisory file locking capability for Python applications. Several different lock mechanisms are supported using a common API.

What is the use of .lock file?

A LOCK file is a file used by various operating systems and programs to lock a resource, such as a file or a device. It typically contains no data and only exists as an empty marker file, but may also contain properties and settings for the lock.


1 Answers

I found flufl.lock most suited for my requirement.

Quoting the author from project page:

[...] O_EXCL is broken on NFS file systems, programs which rely on it for performing locking tasks will contain a race condition. The solution for performing atomic file locking using a lockfile is to create a unique file on the same fs (e.g., incorporating hostname and pid), use link(2) to make a link to the lockfile. If link() returns 0, the lock is successful. Otherwise, use stat(2) on the unique file to check if its link count has increased to 2, in which case the lock is also successful.

Since it is not part of the standard library I couldn't use it. Also, my requirement was only a subset of all the features offered by this module.

The following functions were written based on the modules. Please make changes based on the requirements.

def lockfile(target,link,timeout=300):                                             
        global lock_owner                                                          
        poll_time=10                                                               
        while timeout > 0:                                                         
                try:                                                               
                        os.link(target,link)                                       
                        print("Lock acquired")                                      
                        lock_owner=True                                            
                        break                                                      
                except OSError as err:                                             
                        if err.errno == errno.EEXIST:                              
                                print("Lock unavailable. Waiting for 10 seconds...")
                                time.sleep(poll_time)                              
                                timeout-=poll_time                                 
                        else:                                                      
                                raise err                                          
        else:                                                                      
                print("Timed out waiting for the lock.") 

def releaselock(link):                          
        try:                                    
                if lock_owner:                  
                        os.unlink(link)         
                        print("File unlocked")   
        except OSError:                         
                print("Error:didn't possess lock.")

This is a crude implementation that works for me. I have been using it and haven't faced any issues. There are many things that can be improved though. Hope this helps.

like image 107
gaganso Avatar answered Oct 17 '22 23:10

gaganso