Странное поведение с Perl NFS Lock

У меня странное поведение File::NFSLock в Perl v5.16. Я использую устаревшую опцию тайм-аута блокировки как 5 минут. Допустим, у меня есть три процесса. Одному из них потребовалось более 5 минут, прежде чем снять блокировку, и процесс 2 получил блокировку. Однако даже процесс 2 имеет блокировку менее 5 минут, приходит третий процесс и удаляет файл блокировки, что приводит к сбою второго процесса при удалении NFSLock, удерживаемого самим собой.

Моя теория гласит, что процесс 3 ошибочно считывает время последнего изменения блокировки как время, записанное процессом 1, а не процессом 2. Я пишу блокировку nfs для разделов, смонтированных на NFS.

У кого-нибудь есть идея или сталкивался с подобной проблемой с Perl NFSLock? Пожалуйста, обратитесь к приведенному ниже снимку

my $lock = new File::NFSLock {file      => $file,
                              lock_type => LOCK_EX,
                              blocking_timeout   => 50,     # 50 sec
                              stale_lock_timeout => 5 * 60};# 5 min

$DB::single = 1;
if ($lock) {
    $lock->unlock()
}

Если я блокирую в точке отладки для процесса 1 более 5 минут, я наблюдаю это поведение


person Deepanshu Arora    schedule 25.02.2020    source источник
comment
Продемонстрируйте получение преждевременной блокировки.   -  person ikegami    schedule 25.02.2020
comment
@ikegami я добавил соответствующий код   -  person Deepanshu Arora    schedule 25.02.2020
comment
Этот код не запускается и даже не проверяет, была ли получена блокировка. Моя просьба в силе.   -  person ikegami    schedule 25.02.2020


Ответы (1)


Из обзора кода на
https://metacpan.org/pod/File::NFSLock
Я вижу, что Блокировка реализована просто физическим файлом в системе.
Я работаю почти в каждом проекте с одной и той же логикой блокировки процесса.

При блокировке процесса крайне важно не устанавливать stale_lock_timeout слишком жестко.
В противном случае возникнет «состояние гонки», о чем также упоминается в комментариях к коду.

Как вы упомянули, 3 процесса начинают конкурировать за одну и ту же блокировку, потому что задание занимает > 5 минут, и вы устанавливаете tale_lock_timeout на 5 минут.
Если у вас есть исправление времени, например crond Service this будет запускать процесс каждые 5 минут. Каждый процесс будет воспринимать блокировку как устаревшую, поскольку уже прошло 5 минут, хотя процесс занимает более > 5 минут.

Чтобы описать возможный сценарий:
Выполнение некоторых заданий БД занимает 4 минуты, но в перегруженной системе это может занять до 7 минут и более.
Теперь, если служба crond запускает процесс каждые 5 минут
В 0 минут первый процесс process1 найдет задание как новое, установит блокировку и запустит задание, что займет до 7 минут.
Теперь в 5 минут служба crond запустит process2, которая найдет блокировку process1, но решит, что она уже устаревшая, поскольку с момента создания блокировки прошло уже 5 минут, и она будет принята как устаревшая. Таким образом, process2 снимает Блокировку и запрашивает ее для себя.
Позже, в 7 минут process1 уже завершил Задание и, не проверяя, является ли это все еще его Блокировкой, снимает Блокировку process2 и завершает работу.
Теперь в 10 минут process3 запускается и не находит никакой блокировки, поскольку блокировка process2 уже снята process1 и устанавливает свою собственную блокировку.
Этот сценарий на самом деле очень проблематичен. потому что это приводит к накоплению процесса и накоплению рабочей нагрузки и непредсказуемым результатам.

Предложение по устранению этой проблемы:

  1. Установите stale_lock_timeout на сумму, намного большую, чем потребовалось бы для задания (например, 10 или 15 минут). stale_lock_timeout, но больше, чем график выполнения.
  2. Расширьте расписание выполнения, чтобы у каждого процесса было достаточно времени для завершения своей задачи (каждые 10 минут или каждые 15 минут).
  3. Рассмотрите возможность интеграции задания process1, process2 и process3 в одно только process_master, которое запускает каждый процесс после завершения предыдущих раз.
person Bodo Hugo Barwich    schedule 26.02.2020
comment
Re Позже, через 7 минут, процесс1 уже завершил задание и, не проверяя, является ли он по-прежнему его блокировкой, снимает блокировку процесса2 и завершает работу. Мне это кажется большим! - person ikegami; 26.02.2020
comment
Re Этот сценарий на самом деле очень проблематичен, потому что он приводит к накоплению процессов. Да, устаревшее задание должно быть уничтожено. - person ikegami; 26.02.2020
comment
В моем случае вы можете предположить, что process3 работает с другим кодом и может только снять блокировку. процесс 2 ждал, пока процесс 1 освободит блокировку, и начал выполнение, а процесс 3 запустился в определенный момент и снял блокировку, принадлежащую процессу 2. - person Deepanshu Arora; 27.02.2020
comment
@ikegami, поскольку сценарий на самом деле не устарел. просто в некоторых обстоятельствах это занимает больше времени, чем ожидалось. stale_timeout слишком мал и не соответствует окну реального времени выполнения задания - person Bodo Hugo Barwich; 27.02.2020
comment
@DeepanshuArora система, как вы описываете, process3 может только снять блокировку и требует завершения process1 и process2, запрашивает архитектуру process_master, которая запускает process1, process2 и, наконец, process3. Но в любом случае, вы уже пытались установить stale_timeout больше, как я предложил? - person Bodo Hugo Barwich; 27.02.2020
comment
@DeepanshuArora Опять же, когда мы продолжим анализировать описанную систему, имеет ли смысл выполнять process3 при неудачном process2, если он полагается на то, что process1 и process2 добились успеха? Опять же, архитектура process_master может заставить систему работать. - person Bodo Hugo Barwich; 27.02.2020
comment
@Bodo Hugo Barwich, Re, как сценарий, задание на самом деле не устарело., я сказал устарело, а не зависло. Задание считается устаревшим, если оно все еще выполняется по истечении stale_timeout секунд по определению. - person ikegami; 27.02.2020
comment
@ Бодо Хьюго Барвич, Re stale_timeout слишком мал, это не имеет отношения к тому, что я говорил. Даже если вы увеличите stale_timeout, вам все равно придется убить существующее задание, если вы запустите еще один его экземпляр! - person ikegami; 27.02.2020
comment
@ikegami безразличный kill process2 можно рекомендовать только в том случае, если все процессы являются экземплярами одного и того же сценария, производящего один и тот же полностью воспроизводимый результат (например, некоторая проверка is-alive). Но в случае использования, когда они являются частью инкрементного, прогрессивного задания (например, резервного копирования базы данных или загрузки больших файлов), kill process2 приведет к ошибке и повреждению результат всей работы. Поэтому это не первый мой ответ. - person Bodo Hugo Barwich; 28.02.2020