Python: блокировка текстового файла в NFS

У меня есть файл results.txt на сервере, к которому обращаются несколько виртуальных машин через NFS. На каждой из этих виртуальных машин запускается процесс, который считывает файл results.txt и изменяет его. Если два процесса, A и B, читают файл одновременно, то модификация A или B будет присутствовать в results.txt в зависимости от порядка записи процессов в файл.

Если процесс A имеет блокировку записи в файле, то процессу B придется ждать снятия блокировки, чтобы прочитать файл results.txt.

Я попытался реализовать это с помощью Python:

import fcntl


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

Он работает должным образом для файлов на локальном диске.

но когда я запускаю попытку заблокировать файл по смонтированному пути, я получаю следующую ошибку:

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

Я пробовал fcntl.fcntl и fcntl.flock, но получил ту же ошибку. Это проблема с тем, как я использую fcntl? Требуется ли какая-либо настройка на сервере, где хранится файл?

Изменить:

Вот как я использую 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)

Версия сервера NFS — 3.


person gaganso    schedule 04.06.2016    source источник
comment
Не могли бы вы вставить так, как вы используете fcntl.fcntl()?   -  person vmonteco    schedule 09.06.2016
comment
@vmonteco, я добавил фрагмент кода.   -  person gaganso    schedule 09.06.2016
comment
fcntl.fcntl(f, fcntl.LOCK_EX) работает?   -  person vmonteco    schedule 09.06.2016
comment
@vmonteco Это не дает никаких ошибок. Но я не думаю, что блокировка применяется. Я могу запустить скрипт для одного и того же файла в двух разных сеансах, и ни один из них не ждет.   -  person gaganso    schedule 09.06.2016


Ответы (1)


Я нашел flufl.lock, наиболее подходящий для моих требований.

Цитирую автора с страницы проекта:

[...] O_EXCL не работает в файловых системах NFS, программы, которые полагаются на него для выполнения задач блокировки, будут содержать состояние гонки. Решение для выполнения атомарной блокировки файла с помощью файла блокировки состоит в том, чтобы создать уникальный файл в той же файловой системе (например, включая имя хоста и pid), использовать link(2) для создания ссылки на файл блокировки. Если link() возвращает 0, блокировка прошла успешно. В противном случае используйте stat(2) для уникального файла, чтобы проверить, увеличилось ли количество его ссылок до 2, и в этом случае блокировка также будет успешной.

Поскольку он не является частью стандартной библиотеки, я не мог его использовать. Кроме того, мое требование было лишь подмножеством всех функций, предлагаемых этим модулем.

Следующие функции были написаны на основе модулей. Пожалуйста, внесите изменения в соответствии с требованиями.

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.")

Это грубая реализация, которая работает для меня. Я использовал его и не сталкивался с какими-либо проблемами. Хотя есть много вещей, которые можно улучшить. Надеюсь это поможет.

person gaganso    schedule 07.07.2016