Верстакфорум практиков
рекламаiprazon: приватные серверные адреса IPv4 и SOCKS5, безлимитный трафик, бесплатный тест до 2 часов
ФорумСкрипты и автоматизация

Очередь задач на обычном файле, когда база лишняя

queue_master
queue_master
Участник
сообщений 410
с ноя
1 мая, 08:52первое сообщение

Есть маленький прогон: список заданий на несколько тысяч строк, четыре рабочих процесса на одной машине. Заводить под такое базу рука не поднимается, хочется обойтись файлами.

Наивный вариант ломается сразу. Читать первую строку и вычёркивать её через sed -i четыре процесса одновременно не умеют: файл переписывается целиком, и половина заданий испаряется вместе с чужой правкой.

Как люди делают очередь на файлах, чтобы без потерь и без базы?

очередь длиннее памяти
Костя Фомин
Костя Фомин
Знаток
сообщений 1600
с апр
8 октября, 11:09#2

Файл тут не нужен вовсе, нужен каталог. Одно задание это один файл, а состояние задания это имя каталога, в котором файл лежит.

ochered/
  novye/     zadacha-0417.json
  v-rabote/
  gotovo/
  bityye/

Рабочий забирает задание переименованием из novye в v-rabote. Переименование внутри одной файловой системы атомарно: либо получилось, либо файла там уже нет. Два процесса за один файл не подерутся, проигравший получит ошибку и спокойно возьмёт следующий.

sonyaK
sonyaK
Новичок
сообщений 240
с фев, второй сезон
15 марта, 14:26#3

а как рабочий выбирает, какой файл брать? если все четверо пойдут за первым по алфавиту, трое получат ошибку

queue_master
queue_master
Участник
сообщений 410
с ноя
22 августа, 17:43#4
Костя Фомин: проигравший получит ошибку и спокойно возьмёт следующий

Вот это и смущает. На четырёх рабочих ещё терпимо, а станет их двадцать, и они будут драться за одну и ту же голову очереди на каждом шаге.

очередь длиннее памяти
Артур Вагнер
Артур Вагнер
Знаток
сообщений 1290
с мая
1 января, 08:00#5

Драка снимается перемешиванием. Рабочий читает список, тасует его и идёт в своём порядке, тогда вероятность столкнуться падает до мелочи.

vzyat_zadachu() {
  for f in $(ls ochered/novye | shuf); do
    if mv "ochered/novye/$f" "ochered/v-rabote/$f.$$" 2>/dev/null; then
      printf '%s\n' "ochered/v-rabote/$f.$$"
      return 0
    fi
  done
  return 1
}

Номер процесса в имени пригодится дальше. Рабочий умер, его файлы остались в v-rabote, и уборщик по расписанию возвращает в novye всё, что старше получаса и чей процесс уже мёртв.

Второй приём для тех, кому четыре каталога кажутся грубыми: журнал с дописыванием. Файл только растёт, каждая строка это событие, взято или сделано. Состояние очереди собирается прочтением журнала с начала. Короткая строка под O_APPEND дописывается атомарно, поэтому четыре процесса пишут в один файл спокойно. Минус тоже понятный: журнал надо периодически сворачивать, иначе он пухнет без конца.

Каталог понятнее глазами, журнал быстрее на мелких задачах. На нескольких тысячах строк я бы взял каталог и не думал.

конфиг без комментария это загадка
hardline
hardline
Знаток
сообщений 970
с июн
8 июня, 11:17#6

Одно уточнение к переименованию. Атомарность работает внутри одной файловой системы. Если novye и v-rabote оказались на разных точках монтирования, mv превращается в копирование с удалением, и вся гарантия испаряется.

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

Костя Фомин
Костя Фомин
Знаток
сообщений 1600
с апр
15 ноября, 14:34#7

Про монтирование верно. Проверяется одной строкой: stat -c '%d' ochered/novye ochered/v-rabote, номер устройства должен совпасть.

queue_master
queue_master
Участник
сообщений 410
с ноя
22 апреля, 17:51#8

Сделал на каталогах. Четыре подкаталога, взятие через mv с перемешиванием списка, номер процесса в имени, уборщик раз в десять минут возвращает зависшие.

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

База под это правда лишняя, спасибо всем.

очередь длиннее памяти