Size: a a a

2020 September 01

AB

Alex Bubnov in pro.elixir
VDimir
Всем привет!

Возник довольно общий вопрос по принципам организации erlang/elixir приложения.
Как правильно организовать параллельный доступ к данным или состоянию? Например, ets или другая хеш таблица, какая-то сущность, доступная для чтения и записи, должна конкурентно читаться несколькими потоками.
Первой идеей было использовать GenServer, в его state хранить данные и отвечать через handle. Тогда весь доступ сериализуется, нет параллельности. Можно сразу после получения запроса отдавать управление из handle с noreply и запускать процесс, который дошлет ответ клиенту, но в таком случае не понятно как синхронизироваться.
В классической модели с потоками решением был бы RWLock, который давал бы одновременное чтение и синхронизировал записи.
Как такую задачу решить в акторной модели?

Интересуют как конкртеные ответы как это сделать опрделенными средствами, напримр, на ets (тут кажется можно пропробовать просто доступаться к таблице по глобальному идентификатору, хотя опять же без синхронизации) . Так и более общие, какие-то паттерны. Причем также интересна специфика в отдельных случаях, например когда есть только чтение, чтение и запись read/write-heavy и т.п.
если нужно синхронизированная запись и несинхронизированное чтение  - перед ets ставится генсервер, через который идет вся запись, это стандартный паттерн.
источник

LL

Lama Lover in pro.elixir
Alex Bubnov
>  three main ones were Ruby, Erlang, and Clojure. Not only did we get features from those languages, but we also got part of their philosophy.
> With Clojure, there are a bunch of similar ways we approach problems, like protocols.

по итогам можно заметить, что недостаточно Валим понимает философии кложи
А почему? В кложе совсем другие протоколы?
источник

V

VDimir in pro.elixir
Alex Bubnov
если нужно синхронизированная запись и несинхронизированное чтение  - перед ets ставится генсервер, через который идет вся запись, это стандартный паттерн.
А читать напрямую?
источник

AB

Alex Bubnov in pro.elixir
VDimir
А читать напрямую?
можно напрямую, можно как-нибудь извращеннее - пул на чтение поставить, например. ума не приложу, зачем, но почему бы и нет.
источник

AB

Alex Bubnov in pro.elixir
Lama Lover
А почему? В кложе совсем другие протоколы?
потому что протоколы только деталь реализации, к философии довольно слабо относящаяся.
источник

V

VDimir in pro.elixir
Alex Bubnov
можно напрямую, можно как-нибудь извращеннее - пул на чтение поставить, например. ума не приложу, зачем, но почему бы и нет.
А если нужно сделать транзакцию несколько записей, как гарантировать что читатели не пролезут? Ставить какой то лок при входе в генсервер? Но читатели ходят напрямую.
источник

LL

Lama Lover in pro.elixir
VDimir
Всем привет!

Возник довольно общий вопрос по принципам организации erlang/elixir приложения.
Как правильно организовать параллельный доступ к данным или состоянию? Например, ets или другая хеш таблица, какая-то сущность, доступная для чтения и записи, должна конкурентно читаться несколькими потоками.
Первой идеей было использовать GenServer, в его state хранить данные и отвечать через handle. Тогда весь доступ сериализуется, нет параллельности. Можно сразу после получения запроса отдавать управление из handle с noreply и запускать процесс, который дошлет ответ клиенту, но в таком случае не понятно как синхронизироваться.
В классической модели с потоками решением был бы RWLock, который давал бы одновременное чтение и синхронизировал записи.
Как такую задачу решить в акторной модели?

Интересуют как конкртеные ответы как это сделать опрделенными средствами, напримр, на ets (тут кажется можно пропробовать просто доступаться к таблице по глобальному идентификатору, хотя опять же без синхронизации) . Так и более общие, какие-то паттерны. Причем также интересна специфика в отдельных случаях, например когда есть только чтение, чтение и запись read/write-heavy и т.п.
Зависит от специфики данных и доступа:
С асинхронным доступом:
Для данных общего вида (типа структуры, тупплы разных размеров и типов), то
ets если на одной ноде
crdt на GenServer, mnesia, crdt на ets если на разных
Если это какие-то инты, то есть atomics и counters
Если это кэш по таймауту/переполнению, то есть Cachex

С синхронным доступом
Можно ets с read_concurrecny / write_concurrency false
Можно генсервер
источник

AB

Alex Bubnov in pro.elixir
VDimir
А если нужно сделать транзакцию несколько записей, как гарантировать что читатели не пролезут? Ставить какой то лок при входе в генсервер? Но читатели ходят напрямую.
ты хочешь прям настоящую транзакцию?
источник

AB

Alex Bubnov in pro.elixir
вообще, транзакции есть в mnesia, можно использовать ее в бездисковом режиме
источник

AB

Alex Bubnov in pro.elixir
но это уже начинает вызывать вопросы, что же ты такое делаешь, и зачем тебе транзакции в памяти
источник

V

VDimir in pro.elixir
Lama Lover
Зависит от специфики данных и доступа:
С асинхронным доступом:
Для данных общего вида (типа структуры, тупплы разных размеров и типов), то
ets если на одной ноде
crdt на GenServer, mnesia, crdt на ets если на разных
Если это какие-то инты, то есть atomics и counters
Если это кэш по таймауту/переполнению, то есть Cachex

С синхронным доступом
Можно ets с read_concurrecny / write_concurrency false
Можно генсервер
Cachex выглядит прикольно, он поверх ets как я понял. А что такое atomics и counters? не получается нагуглить
источник

AB

Alex Bubnov in pro.elixir
VDimir
Cachex выглядит прикольно, он поверх ets как я понял. А что такое atomics и counters? не получается нагуглить
atomics и counters это стдлиб эрланга
источник

AB

Alex Bubnov in pro.elixir
то есть, часть beam, через него выставленная
источник

V

VDimir in pro.elixir
Alex Bubnov
ты хочешь прям настоящую транзакцию?
Как пример. Локами это делается легко, интересно проследить параллель, как это может выглядеть в beam? Не обязательно память, это могут быть какие то внешние апи, для которых нужно сохранить инварианты
источник

AB

Alexey Bolshakov in pro.elixir
Есть даже пример, как на базе этих атомик каунтеров, можно сделать семафор
источник

AB

Alex Bubnov in pro.elixir
Alexey Bolshakov
Есть даже пример, как на базе этих атомик каунтеров, можно сделать семафор
но делать так, конечно же, не стоит
источник

V

VDimir in pro.elixir
Пока что понимаю так, что если нужна синхронизация, то оборачиваю в генсервер или какой то воркер, который будет сохранять инварианты, а если параллельно, то вызывать методы апи напрямую (того же ets если речь про данные в памяти)
источник

V

VDimir in pro.elixir
Lama Lover
Зависит от специфики данных и доступа:
С асинхронным доступом:
Для данных общего вида (типа структуры, тупплы разных размеров и типов), то
ets если на одной ноде
crdt на GenServer, mnesia, crdt на ets если на разных
Если это какие-то инты, то есть atomics и counters
Если это кэш по таймауту/переполнению, то есть Cachex

С синхронным доступом
Можно ets с read_concurrecny / write_concurrency false
Можно генсервер
А crdt нужно делать самому или искать стороннюю реализацию, встроенного ничего нет? Есть ли хорошие реализации?
источник

AB

Alex Bubnov in pro.elixir
VDimir
Пока что понимаю так, что если нужна синхронизация, то оборачиваю в генсервер или какой то воркер, который будет сохранять инварианты, а если параллельно, то вызывать методы апи напрямую (того же ets если речь про данные в памяти)
да, общепринятый способ сериализации - через процессы.
насчет сохранения инвариантов - это очень далекоидущая тема, в которой непонятно, где останавливаться. например, так можно добраться до sagas, чем не вариант.
источник

V

VDimir in pro.elixir
И atomic это именно мутабельная глобальная переменная получается? Прочитал доку, выглядит чужеродно для эрланга, не думал что прям в таком виде будет, ровно как и в языках с тредами
источник