Pull to refresh

Comments 28

Кейсы с блокировкой требуются в крон-задачах и почти никогда при обычных запусках. Если учитывать это — думаю лучшим вариантом было бы просто написать простенький адаптер под уже существующее несколько лет решение, которое использует симфонийский консольный компонент: https://github.com/illuminate/console


Оно уже покрывает все проблемы, озвученные в статье.


$sheduler->command('some')->everyFiveMinute()->withoutOverlap();

М?

Безусловно, но это решение значительно больше, чем 2 коротких файла.
Да и в моем случае это не крон.

Я подозреваю, что вполне сопоставимо будет. Все команды уже отнаследованы от симфони, всё что потребуется — это написать бандл, который их будет регать в сервисах с нужным тегом.


С другой стороны — согласен, нужен ресёрч, я сходу не могу представить как добавить сервис в "зафризенный" симфонёвый контейнер, кроме как выполнить ещё раз его билд. Да и возможно могут возникнуть проблемы с контейнером, в симфони у него нет функционала двойной диспатчеризации и получения сервиса по интерфейсу, только сервислокация и автовайринг.


Короче, да, согласен, надо смотреть. В качестве быстрого решения на коленке — ваш вариант оправдан более чем.+

    public function lock($name)
    {
        $file = $this->getFilePath($name);
        if ($this->fileSystem->exists($file)) {
            return false;
        }
        $this->fileSystem->touch($file);
        return true;
    }


Если команда сломалась и ваш лок-файл остался, то команда больше никогда не будет вызвана.
Например, если процесс будет убит из консоли. Или выключится электричество. Или произойдет какой-то сбой на сервере.
Да, в этом случае команда не запустится. Это стоит учитывать.
В моем случае есть сборщик мусора, который проверяет «залежавшиеся» лок-файлы и сообщает о том, что это подозрительно. Как ни крути — при солидном кол-ве серверов такое бывает частенько, чем-то жертвовать приходится.
Подумаю над Вашим замечанием, может быть придет идея, спасибо.
UFO just landed and posted this here
Я давненько не встречал таких девайсов, которым нужно так много времени между проверкой и созданием, чтобы другой «вклинился».
Но, в принципе, это можно поправить.
Скорость проверки значения не имеет. Race condition все равно есть. По ссылке пример вообще в памяти, которая куда быстрее «таких девайсов»
Поправил на чуть более удачный вариант.

А зачем в абстрактном классе


    /** @var string */
    protected $name = null;

?
Можно же использовать \Symfony\Component\Console\Command\Command::getName

Действительно, как-то я не заметил :) Осталось после «причесывания». Поправлю, спасибо.

Я бы еще конструктор сделал идентичный базовому:


    /**
     * SingletonCommand constructor.
     * @param LockService $lockService
     * @throws \Exception
     */
    public function __construct(LockService $lockService, $name = null)
    {
        $this->lockService = $lockService;
        parent::__construct($name);
    }
Поправил, спасибо

Я стесняюсь спросить, а что это за юс-кейсы такие странные? Один процесс на N серверов?


Что это таким путём надо делать? Не проще ли это порешать очередью, где можно сколько угодно консумеров запускать, но отрабатывать они могут по одному друг за другом?

Завидую Вашему опыту :) Видимо, Вы еще никогда не слышали отказов типа: «заказчик пока не видит смысла уходить с php 5.3», «мы пока не можем поставить gearman, еtс», «этот модуль поставить нельзя, у нас один php-билд для всех проектов», «да, эта штука мертва уж 5 лет, но у нас есть приоритетнее задачи» и тому подобного.
Безусловно, в стартап-ах и молодых проектах есть возможность не задумываться о таком, но мои статьи в основном связаны с проектами-тинейджерами, где все не так просто.

@jced и всё-таки что за юс-кейсы то такие?

Невозможность установки серверов очередей. Архитектура такова, что есть сервера с шаренной директорией, туда «сбрасываются» на лету сгенерированные скрипты, которые нужно выполнять. На каждом сервере — по демону, которые «рахватывают» эти скрипты-джобы и выполняют. Как-то так, в общих чертах.
Невозможность установки серверов очередей.

Наше вам сочувствие.


Я писал микросервис для лицо-распознавания на питонах и вместо скучного REST'а сделал AMQP-консумера. Это оказалось просто и эффективно. Особенно помогло в горизонтальном масштабировании — можно было запустить кучу консумеров где угодно и задачи отрабатывались быстрее.

Да, в моем случае отличных готовых решений хоть отбавляй, если бы была возможность, но чтобы не городить что-то еще запутаннее чем есть в текущих условиях — решил найти самое короткое решение.
Как мне показалось — один файл (без учета интерфейса), довольно «изящно», решил, может есть такие же как я, застрявшие в 20 веке и им тоже пригодится :)
Лично я сейчас на проекте использую возможности MySQL для блокировок конкурентных запусков команд, а именно функции GET_LOCK(), IS_FREE_LOCK(), IS_USED_LOCK() и RELEASE_LOCK() которые били доданы где то в MySQL 4.1.

Плюсы: Простота, не нужно подчищать lock файлы, так как в случае падения/завершения команды блокировка автоматически удалится. Также есть возможность ожидания освобождение блокировки.
Минусы: не дружит с репликацией.
И второй минус — нужен MySQL :)
UFO just landed and posted this here
на фри хостингах нету проблемы создания блокирующихся тасков. А там где и когда эта проблема может возникнуть явно уже не фри хостинг.
UFO just landed and posted this here
Можно было бы сделать console command listener, завязавшись на console.command, тогда бы не пришлось переписывать текущие команды

Это типа на нескольких серверах, а не на одном.

Это для разных серверов и, как я написал, я пытался уйти от реализации блокировки там, где вызывается команда.
Sign up to leave a comment.

Articles