нужно сделать атомарным. Окай помещаем эти данные (назовем их A) в стейт GenServer.
Далее, другому процессу нужно прочитать A, обработать в зависимости от уже своих данных (назовем их B) и в зависимости от результатов обработки внести изменения в A. И все это надо сделать атомарно.
Получается, что для атомарности, надо передать данные B генсерверу хранящему в стейте данные A, а он уже сам проведет эти обработки и внесет изменения. Казалось бы круто, но беда в том, что данные B имеют достаточно большой объем и представляется нецелесообразным копировать их из процесса в процесс.
Неужто придется таки городить мютекс?
Есть очень много способов решения этой проблемы: 1. Если B может хранить свои данные в области литералов (они очень редко меняются или их можно записать при компиляции), то можно в GenServer A передать анонимную функцию, которая приймет данные A и вернет их модифицированную версию. Копирования при этом не будет. 2. Можно использовать ETS для A с сериализацией доступа. Тогда B может прочитать, мутировать и записать изменения A. Но там может прийтись придумать механизм хендоффа таблицы (можно подсмотреть в коннекшин пулинге Ecto как это делается). 3. Можно сделать механизм блокировки на уровне генсервера, который позволит сделать checkout данных из A по похожему принципу с тем, как работает чекаут в коннешин пулинге. (Только нужно будет мониторить процесс который сделалю чекаут чтобы автоматически завершить лок если он крешнулся или подумать про то как процессы зависят в древе супервайзеров). Вообще задача слишком абстрактная, не понятно много - это как много, что за данные, насколько дорогие/долгие вычисления и т.п.
Некоторое подобие оптимистического лока можно организовать на ets: select_replace
Все три способа мне известны. 3-й способ как раз и есть фактически мютекс. 1-й не подходит, данные меняются часто и засунуть их в литерал или в какой-нибудь :persistent_term тоже не ахти идея. Выбор между ets или даже mnesia (транзакций ради) и мютексами. Ну или чем-то еще чего я не знаю.
Почему первому способу нужно редко меняющиеся данные?
Потому что :persistent_term для изменения данных должен сделать глобальный GC
В :persistent_term не рекомендуется пихать часто меняющиеся данные, нет?
Менеджер памяти внутри использует ссылки, так что скорее всего при передаче параметра копирования и не будет вовсе.
Будет, еще как будет. У меня там нет бинарей вообще.
Только большие binary которые не помещаются в heap процесса
Сделать это неатомарно не позволит сама модель акторов. Вы хотите сериализуемость? Может она не так нужна и пойдёт модель консистентности помягче?
Еще как позволит. Прочитал данные, пока идет обработка другой процесс тоже прочитал. Потом первый записал, затем второй записал. Приплыли. Гонки завершились победой одного из процессов.
А не проще все необходимые данные перенести в один процесс чтобы обработать сразу в одном процессе, а не гонять туда-сюда лямбды или данные?
Видимо есть код B который не зависит от А и наоборот
Ну, можно реализовывать чтение перед записью Процесс получил копию данных из агента, изменил их. Потом атомарно проверил копию и то, что лежит в агенте, и записал если они равны
А если не равны, то снова прочитал и опять обработал и попытался записать и так далее? :)
Да, оно так и работает. Понятное дело, что у этого способа есть свои ограничения, но он очень хорошо работает, когда память делят мало акторов, а операция очень резвая. А про то что операция может не завершиться — это возможно и с мьютексом, конечно же. Если есть вероятность что доступ очень частый, то нужно реализовать очередь задач
Эм с мютексом-то как операция не сможет завершиться?
Ну, актор хочет взять мьютекс, а он уже взят. Актор опять хочет взять мьютекс, а он уже взят И т.д. Типа в перерывах между попытками взять мьютекс, кто-то другой его отпускает и берёт
Не обязательно, актор пытается залочить мютекс и блокируется пока мютекс не освободится.
А как он поймёт что мьютекс освободился?
Мютекс ему и сообщит, что теперь он овнер.
Так это и есть очередь задач. Просто ты не задачи посылаешь, а входишь в транзакцию. Идея паттерна очередь не меняется
По мне так это просто реализация мютекса. Можно спинлоками, можно очередями.
Только очередь не задач а мутекс-локеров
Обсуждают сегодня