LINUX.ORG.RU

Семафор в postgresql

 ,


0

2

Положим если мы хотим отделить действия двух воркеров в SQL (работает либо один воркер либо другой), то мы можем использовать «mutex» - использовать транзакции и SELECT FOR UPDATE для взаимных блокировок на какой нибудь сторонней строке (положим воркеры с этой строкой не работают а используют ее только для синхронизации, для реализации «mutex»).

А что если мы хотим отделить действия двух групп воркеров в SQL (работает либо одна группа воркеров либо другая)? как бы вы изобрели «semaphore» для этого (любыми средствами SQL)?

Пока думаю в сторону SELECT FOR SHARE и SELECT FOR UPDATE на специальных строках для синхронизации групп воркеров, но до конца что-то пока не сложил 2 + 2. А как бы это сделали вы (именно средствами SQL)?

1. читать из таблицы блокировок первую строку

2а. если в ней значение группы = текущая группа или нет строк, начать транзакцию, добавить в таблицу блокировок строку с номером процесса и текущей группой, закончить транзакцию, выполнить работу, удалить строку с номером процесса из таблицы блокировок

2б. иначе прервать процесс

monk ★★★★★ ()
Ответ на: комментарий от monk

или нет строк

для этого нужно сначала lock table сделать чтобы гонки не было, кроме того процесс может умереть и не убрать за собой. проверять его pid? он мог умереть и его pid мог взять другой (если очень долго не проверять)

quester ★★ ()
Ответ на: комментарий от monk

я придумал другой вариант:

заводим вспомогательную строку как в «mutex» (положим строка в таблице одна для простоты). в строке имя группы, например A или B.

любой процесс:

1) начинаем транзакцию
2) делаем select group_name from semaphore for share;
3) если группа наша - работаем, завершаем транзакцию и выходим
4) если группа не наша - делаем select group_name from semaphore for update skip locked;
5) если ничего не вернулось значит чужие воркеры есть - завершаем транзакцию и выходим
6) если вернулось значение значит чужих воркеров нет - значит мы получили лок.
7) делаем update строки меняя имя группы, завершаем транзакцию и выходим (не работаем чтобы не блокировать коллег по своей группе)

quester ★★ ()
Последнее исправление: quester (всего исправлений: 1)
Ответ на: комментарий от quester

исправленный вариант:

заводим вспомогательную строку как в «mutex» (положим строка в таблице одна для простоты). в строке имя группы, например A или B.

любой процесс:

1) начинаем транзакцию
2) делаем select group_name from semaphore;
3) получаем группу
4) если группа не наша - переходим к шагу 8
5) пока группа наша, лочим - select group_name from semaphore for share;
6) получаем группу
4) если группа не наша - была гонка, завершаем транзакцию и выходим
7) группа наша - работаем, завершаем транзакцию и выходим
8) группа не наша - делаем select group_name from semaphore for update skip locked;
9) получаем группу
10) если ничего не вернулось значит чужие воркеры есть - завершаем транзакцию и выходим
11) если вернулось значение значит чужих воркеров нет - значит мы получили лок.
12) делаем update строки меняя имя группы, завершаем транзакцию и выходим (не работаем чтобы не блокировать коллег по своей группе)

quester ★★ ()
Ответ на: комментарий от quester

тут есть два минуса:
1) периодический update строки. даже если строка в таблице одна - индекс будет пухнуть. хотя индекс можно вообще удалить в таком случае
2) воркеры одной группы простаивают пока работают воркеры другой группы. это серьезная ботва. нужно менять поведение одной группы воркеров вместо того чтобы делить воркеры на две группы

quester ★★ ()
Последнее исправление: quester (всего исправлений: 2)
Ответ на: комментарий от quester

периодический update строки.

это решается добавлением еще специальных «опорных» строк для каждой из групп чтобы каждая из групп пыталась лочить своего врага, тогда собственно update не нужен

quester ★★ ()
Ответ на: комментарий от monk

Я делал примерно по мотивам сообщения Монка, при этом для работы с таблицами блокировок блокировал её эксклюзивно на короткое время с таймаутом, в автономной, что ли транзакции. Или на клиенте.

Да, если клиент умер, то нужно снимать блокировку руками. Блокировка таблицы блокировок снимется сама, а вот блокировка группы воркеров - не снимется.

Т.е. это уже не чисто средствами SQL. Есть впечатление, что голым SQL этого не сделать. И что в общем случае оно всё равно может работать ненадёжно.

den73 ★★★★★ ()
Последнее исправление: den73 (всего исправлений: 2)

Во имя Грязного Хака: ethernet-style

Спросить о «чужих» в списке присоединённых клиентов(нужно как-то отличать, например по логинам или адресам). Если никого нет - работать. Иначе - отсоединиться и проспать случайное время перед повторной попыткой.

DonkeyHot ★★★★★ ()