Бессмысленно внушать представление об аромате дыни человеку, который годами жевал сапожные шнурки.
— Виктор Шкловский, если верить Довлатову
Поскольку среди тех, кому нравится мой стиль изложения, все еще попадаются люди, не имеющие представления о парадигмах внешнего мира, я решил буквально на пальцах показать, что такое акторная модель, и почему познавшие удовольствие работы с ней крайне неохотно отказываются от неё в пользу больших гонораров и душных офисов.
Рассказ рассчитан на тех, кто хотя бы поверхностно знаком с концепциями ООП и (или) ФП. Ниже вы не найдёте всех тех запутывающих псевдонаучных объяснений, которые вам услужливо предоставит Вика или Анжела (или как там вы называете свою любимую LLM в приватных чатиках).
Текст написан именно сегодня, когда Алану Каю исполнилось 85! Поздравляем, Алан, ты — гений, спасибо тебе за всё!
Краткий исторический экскурс
Отцом акторной модели считается Карл Хьюитт, степень популярности которого среди моих соотечественников можно описать отсутствием его персональной страницы на русском языке в Вики. Он даже диссер потом про всё это написал. Его идеями вдохновлялся, среди прочих, Алан Кай, при создании Smalltalk, — именно ему мы обязаны термином «ООП» в его первоначальном значении («я уж точно не имел в виду C++»). Вскорости после этого Джо Армстронг сотоварищи создал эрланг — целиком и полностью построенный на акторной модели. Всё это происходило во времена хиппи.
Потом хиппи превратились в морщинистых маргиналов, Гослинг перепридумал виртуальную машину и байткод, появились персональные компьютеры и веб, а распределенные вычисления — так и оставались уделом машзалов с мейнфреймами. Многозадачность в винде была игрушечной (всё равно, как назвать отвертку, пробойник и коловорот, лежащие в одном ящике, — промышленным комбайном для производства отверстий). Создатели языков увлеклись претворением в жизнь перламутровых пуговиц, а всё программное обеспечение за редким исключением оставалось однопоточным, потому что выполнялось на одном процессоре и переключение контекста только добавляло проблем.
Акторная модель, будучи одной из самых математически элегантных концепций в Computer Science (наравне, пожалуй, с теорией категорий и property-based тестированием), пылилась в дальнем углу запертого на потерянный ключ ящика. Потом в каждый утюг стали пихать по шестьдесят четыре процессора с гипертредингом, но привычка — страшная штука, и акторная модель до сих пор остаётся уделом фриков. Даже невзирая на адаптацию в джаве и дотнете.
Так что же это за зверь?
Давайте на секунду вернемся к аланокайному определению ООП. У нас есть объекты с внутренним состоянием. И унифицированный способ доступа к ним (read/write). По сути, всё современное программирование сводится именно к этому, даже если под объектом мы понимает инстанс тайпкласса в хаскеле, или экземпляр объекта User
в джаве. Унифицированный способ доступа тоже может быть любым: это могут быть методы, как в шарпе, или полиморфные функции высшего порядка в идрисе, или даже сообщения, как в эрланге. Если вдуматься, разницы никакой нет.
Если в качестве объектов мы используем изолированные процессы, а в качестве способа доступа — сообщения, мы имеем дело с акторной моделью.
И всё. Никакой высшей математики и астрологии. Всё просто, как увесистая репа в сауне.
Конструктор — или инициализация структуры данных — это старт процесса. Деструктор — его останов (поднимите руки, кто при виде последнего слова сразу представил себе угловатый шестиугольник на блок-схеме). Метод — отправка сообщения. Для наглядности я приведу два куска кода на псевдоязыках с использованием парадигм ООП и АМ. Детали наподобие типов и валидаций опущены ради внятности.
Классическое джавастайл ООП (выдуманный язык Джарп):
class Developer {
property name
property age
constructor(name, age) { this.name = name, this.age = age }
reader getName() { this.name } // read-only
reader method getAge() { this.age }
writer setAge(age) { this.age = age }
}
// Пример использования
master = Developer.new("Alan", 84)
//⇒ object
master.setAge(85)
age = master.getAge() // ⇒ 85
master.delete() //⇒ удалить объект
А вот в акторной модели на выдуманном языке Эликанг:
master =
spawn_process(fn ->
state = %{name: "Alan", age: 84}
receive_loop do
{:set_age, age} -> state.age = age
{:get_age, pid} -> {:age, state.age} ! pid
:stop -> break_loop()
end
end) #⇒ process identifier
{:set_age, 85} ! master # отправить сообщение процессу
{:get_age, self()} ! master # отправить сообщение процессу
age =
receive do # дождаться сообщения от процесса
{:age, age} -> age
end #⇒ 85
:stop ! master # остановить процесс
Код выше написан на псевдоязыке, но это не имеет значения, он должен быть и так понятен: мы запускаем процесс master
, который запускает бесконечный цикл обработки сообщений. Висит там где-то и ждёт (внутри цикла receive_loop
), пока ему кто-то это самое сообщение доставит. Потом матчит сообщение, и, в зависимости от него, предпринимает какие-то действия (изменяет состояние, или высылает сообщение обратно, или завершается).
Не знаю, как вы, а я особых отличий от ООП пока не вижу. spawn_process
вместо constructor
, отправка сообщения вместо вызова мутирующего метода, отправка и получение ответа — вместо чтения.
Тогда зачем?
Преимущества незаметны на выдуманных простых примерах. Создать объект с двумя «полями» и изменять/читать их значения — та задача, которая легко решается даже на ассемблере. Кроме того, пример на АМ получился даже немного многословнее. Но что будет, если объектов 100?
На Джарпе код изменится примерно так:
- master = Developer.new("Alan", 84)
+ master = Developer.read_from_database("Alan")
На Эликанге:
- state = %{name: "Alan", age: 84}
+ state = :db.read("Alan")
Не так-то много отличий, да? — Нет. Посмотрите на скоупы: в акторной модели мы сходим в базу один раз, а потом (пока процесс не помрёт) — наш «developer» будет в «локальном кэше» — в состоянии уже запущенного процесса. Мы можем его изменять, получать из него данные — и всё это без походов в базу. Однажды затребованный «developer» — под рукой всегда. В случае Джарпа — каждый раз, когда нам требуется что-то сделать с объектом «developer» — его сначала нужно откуда-то (из базы) достать. Отсюда все эти N+1
проблемы, красные метрики на базе, ошибки Connection Limit Reached — и прочие никому не нужные радости.
Осталось решить несколько вновь появившихся проблем:
① за процессами кто-то должен следить, потому что если крысы перегрызут кабель — мы не должны потерять наши данные
② процессы надо как-то адресовать (по имени, например), чтобы получить к ним доступ откуда угодно
③ кучу бойлерплейта по отправке/приёмке сообщений надо бы причесать и вынести в абстракции языка
④ нужно уметь адекватно реагировать на невозможность доставки сообщения (процесса нет, он в процессе перезапуска)
⑤ хорошо бы (для прозрачного горизонтального масштабирования), чтобы имена процессов не были бы привязаны к физической машине
⑥ гонки данных — с ними надо что-то делать, давать их на откуп разработчикам нельзя ни при каких обстоятельствах: напортачат-с
Я думаю, что опыт разработчиков Го по созданию вытесняющей многозадачности без виртуальной машины — можно будет скоро использовать для новых языков, построенных на акторной модели. Пока в существующих языках (эрланг, эликсир, gleam, lfe) — ① решается виртуальной машиной. ② и ⑤ закрываются глобальным пространством имён процессов. Ниже я вкратце расскажу, как в эликсир решает проблемы ③, ④ и ⑥.
⑥ → Иммутабельность
Для решения проблемы гонок данных можно было бы навертеть черта лысого в ступе. Но есть очень простое и понятное решение: иммутабельность. Полная иммутабельность языка. Написал foo = 42
— и пока идентификатор foo
не вышел из скоупа — значение переменной будет 42
. Это нечеловечески удобно (причем, не только нам, программистам, — но и сборщику мусора). Медленнее? — На определенном классе задач — да. Этот класс задач уместнее решать на более приспособленных парадигмах с компилятором в нативный код (си, раст, хаскель).
Но в прикладной разработке таких задач исчезающе мало и они все закрыты прозрачными биндингами. Зато «воткнул к двум еще одну ноду и нагрузка снизилась в полтора раза без изменения кода» — бесценно во всякого рода навороченной джейсоноукладке. Один гигантский CSV с валидациями и тяжелой перегруппировкой данных эликсир умеет разбирать не только на всех ядрах, но и на всех нодах в кластере одновременно. Из коробки. Что скажете?
③, ④ →Абстракции для людей
Конечно, каждый раз писать блок receive do
с полным разбором всех возможных ожидаемых сообщений — нормальному человеку в голову не придет. Поэтому люди придумали абстракцию, которая помогает сосредоточиться собственно на обработке сообщений.
В виртуальной машине эрланга (и супертонкой стандартной библиотеке самого языка) — все сообщения по заветам Алана (с днем рождения еще раз!) асинхронные. Отправил — и всё. Никаких гарантий доставки даже.
Но мы легко может эмулировать синхронность добавкой отсылки сообщения «получено» обратно — и обработкой его в исходном процессе. Это всё еще не даёт 100% гарантию (в хорошем сценарии: сообщение → ответ → реакция — даёт), но мы можем не получить положительный ответ. Что ж, вместо того, чтобы добиваться гарантий костылями в этом случае, достаточно просто привыкнуть к их отсутствию. Я аккуратно отрабатываю такие сценарии уже десять лет, хотя еще ни разу не сталкивался с недоставленным подтверждением от вызываемого процесса.
Чтобы было удобно писать именно бизнес-логику, эрланг (и эликсир, конечно) предоставляют возможность паттерн-матчинга везде, включая параметры функций, поэтому код выше будет выглядеть как-то так:
defmodule Developer do
use GenServer # абстракция работы с процессом
def init(name, age, do: {:ok, %{name: name, age: age}}
def handle_cast({:set_age, age}, state) do
{:noreply, %{state | age: age}}
end
def handle_cast(:stop, state) do
{:stop, :normal, state}
end
def handle_call(:get_age, _from, state) do
{:reply, state.age, state}
end
end
# Пример использования:
{:ok, pid} = GenServer.start_link(Developer, ["Alan", 84])
GenServer.cast(pid, {:set_age, 85})
#⇒ :ok → этот вызов асинхронный
GenServer.call(pid, :get_age)
#⇒ 85
GenServer.cast(pid, :stop)
#⇒ :ok
Process.alive?(pid)
#⇒ false
Обратите внимание на то, как обрабатываются разные сообщения в разных clauses функции (это колбэк, который вызывает абстракция GenServer
когда получает асинхронное сообщение) handle_cast/2
.
Процесс можно бесшовно запустить на любой ноде в кластере (например, получить список всех нод и выбрать случайную, раундробинную, или даже привлечь хэшринг). Весь остальной код менять не придется: pid
будет работоспособным, вне зависимости от того, на какой ноде процесс в результате запущен.
Отправьте ему сообщение — и просто дождитесь результата, если он вам нужен.
Вот и всё на сегодня. Надеюсь, мне удалось сделать вопрос «что такое акторная модель» чуть менее загадочным.
Удачного сообщайзинга!
Комментарии (80)
Dhwtj
17.05.2025 08:01Актор это обёртка над изменяемым состоянием, правилами его валидности, умеющая общаться с другими акторами в конкурентной среде, не имеющей гарантий передачи.
Для дошкольников так лучше.
А вот для детского сада:
Представь, что Актор — это такой маленький робот-помощник.
* У каждого робота есть своя коробочка с игрушками (это его "состояние", которое он меняет). Только он сам может играть со своими игрушками и следит, чтобы они были в порядке (это "правила валидности").
* Роботы могут передавать друг другу записочки (это "общение").
* Вокруг много таких роботов, и все они что-то делают одновременно. Робот примет несколько сообщений даже если не успел разобраться с предыдущими. (это "конкурентная среда").
* Когда робот отправляет записочку, он не знает точно, дойдёт ли она и когда (это "нет гарантий передачи").
В ООП методы объектов обычно вызываются напрямую и синхронно (вызвал метод – ждешь выполнения). И не записочками а командами.
Так понятнее? )
SolidSnack
17.05.2025 08:01ООП вообще никак не влияет на синхронность. Вам не кажется странным, впринципе, разделять ООП и ФП? Чем отличается метод от функции? Мне кажется контекстом, отсюда и DDD, бизнес хочет глубже знать разработку, вот пожалуйста готовый подход.
Dhwtj
17.05.2025 08:01ООП вообще никак не влияет на синхронность
Это для примера. Согласен, не точно.
Вам не кажется странным, в_принципе, разделять ООП и ФП?
Мне кажется ещё более странным вводить полиморфизм через ООП как сделано в статье. Сами найдете или вам цитаты искать?
Чем отличается метод от функции?
Метод есть только в ООП и имеет доступ к данным объекта. То есть функция сама по себе, метод только конкретного объекта. И из определения функция (если она не метод) чистая.
Мне кажется контекстом, отсюда и DDD, бизнес хочет глубже знать разработку, вот пожалуйста готовый подход
Не понял
Бизнесу проще говорить в терминах ООП? Спорно. Проще говорить про события, факты и правила, а это всё ФП
SolidSnack
17.05.2025 08:01Объекты легче описать на понятный язык. Представить объект машины в голове и придумать ему свойства и функционал может каждый в бизнесе, а так сказать стать актором, не каждый
cupraer Автор
17.05.2025 08:01Ровно наоборот.
Каждый в этом мире как раз является актором. Получает сообщения от окружающего мира и асинхронно на них отвечает (или не отвечает). Джо Армстронг про это буквально тысячу раз говорил, и я с ним абсолютно согласен.
Объекты — вымученная парадигма, никто в реальном мире не ведет себя, как объект.
SolidSnack
17.05.2025 08:01Каждый в этом мире как раз является актором.
Опишите, пожалуйста, мобильный телефон, его свойства и возможности на акторной модели
cupraer Автор
17.05.2025 08:01defmodule Handy do use GenServer @impl GenServer def init(%{model: %Model{}} = state) do {:ok, %{state | state: :idle}} end @impl GenServer def handle_call({:call, number}, _from, %{state: :in_call}) do {:reply, :busy, state} end def handle_call({:call, number}, _from, %{state: :idle}) do GenServer.send(Dialer, number) {:reply, :started, %{state | state: :in_call}} end @impl GenServer def handle_cast(:ended, state) do {:noreply, %{state | state: :idle}} end end
cupraer Автор
17.05.2025 08:01А вы знаете, что методы классов в питоне — это буквально обыкновенные функции, которые первым аргументом имплицитно получают ссылку на инстанс владельца?
А что методы класса в JS могут очень интересно интерпретировать
this
внутри себя?Мне кажется, в принципе, что ООП странно противопоставлять ФП, но разделять их все-таки имеет смысл. Глядя на код, всегда можно сказать, в ОО или Ф парадигме он написан.
GospodinKolhoznik
17.05.2025 08:01Нет, не понятнее.
Актор — это такой маленький робот-помощник
Кому он помогает и в чём? И насколько он маленький? Это важно, чтобы он был маленьким?
У каждого робота есть своя коробочка с игрушками
Вообще непонятно зачем нужна коробочка с игрушками. Она вводится как что-то явно важное, но потом повествование переключается на записочки а про коробочку больше не вспоминают. Зачем она нужна? Но при этом каждый робот за ней постоянно следит, чтобы коробочка была в порядке - а что значит в порядке?
Робот примет несколько сообщений даже если не успел разобраться с предыдущими.
Стоп. А что значит разобраться? До этого было сказано, что роботы могут передавить друг другу записочки и таким образом общаться. А оказывается, что записочки это не общение, а скорее какие то проблемы с которыми надо разбираться! Да ещё и суетиться надо, так как можно не успеть со всем разобраться. Всё запутано настолько, что жесть!
Когда робот отправляет записочку, он не знает точно, дойдёт ли она и когда
Да уже не важно. В любом случае, кто пытается понять это объяснение, к этому моменту уже поймёт, что это какая то дичь, которую невозможно понять. И слова про то, что доставка записочки толи будет, толи нет, толи дождик толи снег, окончательно его убедят в том, что даже не надо пытаться ничего понять.
nv13
17.05.2025 08:01Интересно, а те, кто программируют на эрланге в курсе, что используют акторную модель?) По моим наблюдениям, точно не все, даже из тех кто этот эрланг и otp преподаёт. Вот Go - в любой вики сразу написано, что это имплементация модели последовательных коммуницирующих процессов, или как там их.
Вы сравниваете акторную модель с ООП - насколько это корректно? Акторная - модель, а ООП - методология. Акторная модель вычислений может быть реализована на любом языке программирования и любой его методологией, и не только программно.
Мне кажется было бы корректней рассматривать акторную модель как разновидность потоковых вычислений, когда не поток инструкций определяет порядок выборки и обработки данных, а потоки данных инициируют выполнение тех или иных инструкций. Это способ организации вычислений, или их модель. Ну да, есть языки, предназначенные только для этого, но это не проблема понимания принципов акторной модели, имхо
Dhwtj
17.05.2025 08:01Мне кажется было бы корректней рассматривать акторную модель как разновидность потоковых вычислений, когда не поток инструкций определяет порядок выборки и обработки данных, а потоки данных инициируют выполнение тех или иных инструкций
Много общего. Но у потока нет состояния.
nv13
17.05.2025 08:01Почему нет? Смотря что понимать под потоком. Пришла реализация данных, запустилась какая то функция, по результатам обработки вычислилась какая то переменная, которая используется при следующей инициации. Переменная - состояние актора. Если внутри актора стейт машина - у актора с каждой инициацией обновляется состояние. Поток не вычислительный тред, а сущность, отражающая распространение данных в системе. Акторная модель, описанная тут в контексте ЯП - частный случай потоковой модели организации вычислений.
Dhwtj
17.05.2025 08:01Всегда рассматривал потоковую модель вычислений как stateless. Но если нет, то соглашусь
cupraer Автор
17.05.2025 08:01Вы сравниваете акторную модель с ООП
Покажите, пожалуйста, где именно я сравнивал акторную модель с ООП? Я всего лишь написал код в двух разных парадигмах — и, о чудо, он получился практически одинаковым.
Акторная — модель, а ООП — методология.
ООП тоже всего лишь модель, что бы вам там инфоцыгане ни нарассказывали.
nv13
17.05.2025 08:0125 лет назад не было инфоцыган) А вот исследования различных моделей организации вычислений, в том числе для построения моделей в различных предметных областях были. Если правильно помню, то самый абстрактная модель организации вычислений называлась token based actor model, или как то так. В зависимости от содержания актора и того, что вкладывалось в ярлык, получались более специализированные модели - синхронные и асинхронные, иерархические, гетерогенные, метрического времени и тепе. В частности, INRIA и Беркли проводили конференции и разные специализированные языки пытались изобретать. Касалось правда это не эээ... "десктопного" программирования, а проектирования цифровых систем контроля и управления. В этом контексте ООП лишь методология. Она не отвечает на вопрос как организовать вычисления в программируемой цифровой системе, даже с SOLID и чистой аохитектурой)
cupraer Автор
17.05.2025 08:0125 лет назад не было инфоцыган)
Как это? А Роберт Мартин? :)
В этом контексте ООП лишь методология.
Ой, ладно, это софистика уже. Ну сложилась же с легкой руки Гослинга «модель вычислений, используемая в 99% ООП проектов».
nv13
17.05.2025 08:01Вот поверите, про великого Боба я узнал три года назад, когда у дочери книжку увидел)) а тогда читал Сухомлина и OSE RM в тексте с псевдографикой. Ну и статьи всякие как строятся гетерогенные многопроцессорные системы
cupraer Автор
17.05.2025 08:01Поверю, конечно, это было видно прямо по самому первому комментарию, а после второго — стало вопиюще-очевидно :)
Grigorii_K
17.05.2025 08:01Пока в существующих языках (эрланг, эликсир, gleam, lfe) — ① решается виртуальной машиной.
С недавних пор есть и в Swift'е как часть языка, на мой взгляд очень элегантная. Если коротко: организована на безстековых корутинах выполняющихся на кооперативном пуле, но есть возможность задать отдельным актора кастомного исполнителя с любой средой обработки сообщений.
Компилятор сам проследит из какого к какому контексту происходит обращение. Вызов одного и того же метода будет либо синхронным, если работа осуществляется внутри актора, либо явно асинхронным через корутину, если компилятор обнаружит что пересекается граница актора.
cupraer Автор
17.05.2025 08:01А можно ссылку на пример кода какой-нибудь?
Grigorii_K
17.05.2025 08:01Хорошие примеры с ходу не находятся, скорее наоборот, немало плохих, видимо потому что вещь относительно новая и уникальная в своём исполнении. Самый простой пример можно посмотреть в официальной документации, но к сожалению она безумно скудная на тему акторов.
Swift довольно органично (и инновационно на мой взгляд) сплавляет между собой безстековые корутины и акторную и ООП модели в единую мощную парадигму, и всё это посыпает статичными проверками на потоковую безопасность. На базе этой системы строится уже более продвинутые вещи из разряда:
Глобальные акторы - объединение набора классов в единный "акторный домен". Между собой классы взаимодействуют последовательно и синхронно, обращение из-вне домена - асинхронное.
Кастомные исполнители для локальных акторов - можно каждому актору назначить эксклюзивный поток, либо выделить один поток для набора акторов, либо любая другая логика планировщика на базе очередей и пула потоков, как душе угодно
Distributed actors - работа с акторами как с обычными объектами, но реально отправляющими сообщения, например, другой машине по сети или другому системному процессу. Ни разу не возникло необходимости использовать, но выглядит очень интересно
cupraer Автор
17.05.2025 08:01Огромное спасибо! Это любопытно, я обязательно повожусь.
Я очень ценю то, что вы нашли и потратили время на такое внятное описание.
michael_v89
17.05.2025 08:01наш «developer» будет в «локальном кэше» — в состоянии уже запущенного процесса. Мы можем его изменять, получать из него данные — и всё это без походов в базу.
① за процессами кто-то должен следить, потому что если крысы перегрызут кабель — мы не должны потерять наши данные
① решается виртуальной машинойРасскажите пожалуйста больше подробностей. Как именно это решается виртуальной машиной? Мы не сохраняли изменения в базу, потом сервер с этим процессом отключился, потому что в нем сломался жесткий диск, и что дальше?
cupraer Автор
17.05.2025 08:01Мы не сохраняли изменения в базу, потом сервер с этим процессом отключился, потому что в нем сломался жесткий диск, и что дальше?
Простите, а в вашем мире «БАЗА» — это такое магическое существо, волшебно сохраняющее всё навсегда, безотказно, даже в случае нетворк сплитов и отказов жесткого диска? Откуда у вас такая слепая вера в Великую Базу — и такое недоверие к процессам? Чем процесс хуже?
Вам, возможно, будет любопытно заглянуть в код RabbitMQ и даже местами Kafka. Там нет базы. Но вы их, наверное, могли бы использовать безоглядно, так?
больше подробностей
Есть миллиард способов эту конкретную проблему решить: писать event log, сохранять критические изменения состояний, да хоть черта в ступе, для каждой конкретной задачи — существует своё решение. Но доступ на чтение уж точно может обойтись без похода в базу.
michael_v89
17.05.2025 08:01а в вашем мире «БАЗА» — это такое магическое существо, волшебно сохраняющее всё навсегда
Нет. В моем мире «БАЗА» — это такое приложение, сохраняющее всё в файлы на жесткий диск. Не волшебно, не безотказно, и не навсегда, просто сохраняющее. С намеренно реализованной системой транзакций, которая имеет защиту от сбоев в середине записи. В отличие от состояния процесса, которое находится в оперативной памяти, на жесткий диск не сохраняется, и системы транзакций не имеет.
И являющееся общим источником данных для всех процессов, которые с ней работают. В отличие от процессов на нодах кластера, каждый из которых может загрузить себе из базы локальную копию данных одного и того же объекта.
Откуда у вас такая слепая вера в Великую Базу
Ниоткуда, потому что это не вера и не слепая. А знание о том, как работают базы, и зачем нужны походы в них.
Чем процесс хуже?
Тем, что процессом называют код в оперативной памяти, который изменяет состояние в оперативной памяти, которое по умолчанию на жесткий диск не сохраняется, и системы транзакций не имеет.
Если же процессы в Эрланге транзакционно сохраняют состояние на жесткий диск с защитой от сбоев, то являются базой данных, поэтому ваше утверждение "без походов в базу" ложно.
Бэкапы, распределенные транзакции, и другие особенности использования миллиона разных баз вместо одной пожалуй не будем обсуждать.RabbitMQ и даже местами Kafka. Там нет базы. Но вы их, наверное, могли бы использовать безоглядно, так?
В RabbitMQ и Kafka есть персистентность, для ее корректной работы там написано много кода. Но они не являются источником данных, а просто передают их от источника к получателю. И да, если надо гарантировать отправку сообщений после сохранения изменений, то используется подход transactional outbox, который внезапно подразумевает использование базы данных.
Мы можем его изменять ... и всё это без походов в базу.
Но доступ на чтение уж точно может обойтись без похода в базу.Вы же говорили про "изменять без походов в базу"? Теперь оказывается, что это только для чтения подходит?)
Доступ на чтение без похода в базу и даже изменение прекрасно реализуются без акторов и процессов в любых языках. А походы в базу нужны, чтобы измененное состояние стало доступно другим процессам, в том числе в случае перезапуска изменившего процесса, в том числе самой базы.
писать event log, сохранять критические изменения состояний
А, ну то есть вместо того, чтобы ходить в уже написанную базу, вы предлагаете писать свою базу и ходить в нее. Как-то не выглядит похоже на "без походов в базу". Выглядит как дополнительная работа, а не как преимущество, которым вы пытались его представить.
cupraer Автор
17.05.2025 08:01процесса […который…] системы транзакций не имеет.
процессов на нодах кластера, каждый из которых может загрузить себе из базы локальную копию данных одного и того же объекта
Процесс, конечно же, имеет механизм транзакций. И не бывает двух процессов для одного товара (если только этого зачем-то специально не захотел программист).
В RabbitMQ и Kafka есть персистентность, для ее корректной работы там написано много кода.
Не может быть. Чтобы что-то работало, нужен код.
вместо того, чтобы ходить в уже написанную базу, вы предлагаете писать свою базу
Нет конечно, реализаций Event Log’а полно́ на любой вкус и цвет. Просто обращений на запись к нему будет на порядки меньше и они будут на порядки быстрее стандартной RDBMS (и NoSQL тоже, но там все не так прозрачно).
———
Короче, я понял ваш основной тезис: диск — хорошо, память — плохо. Медленнее, у́же бутылочное горлышко, в разы хуже и сложнее масштабируется, но — лучше.Я не понимаю, с каких херов, и уж подавно не разделяю этот тезис, но, как минимум — понял позицию.
michael_v89
17.05.2025 08:01Процесс, конечно же, имеет механизм транзакций.
Ну вот с этого и надо было начинать. "Не используйте одни транзакции, используйте другие".
Нет конечно, реализаций Event Log’а полно́ на любой вкус и цвет.
А он сам магически будет писать то что мне нужно, или надо писать какой-то код для его вызова в нужные моменты с нужными параметрами? Я, естественно, подразумевал использование сторонней библиотеки, а не написание всего с нуля.
База это в общем-то и есть одна из таких реализаций. Я ей говорю что надо изменить, она сама пишет нужные логи для защиты от сбоев.И не бывает двух процессов для одного товара (если только этого зачем-то специально не захотел программист).
У нас 2 ноды, которые обрабатывают веб-запросы, обе запросили товар 321, процесса для которого еще нет. Они обе выполняют
state = :db.read(321)
, запускают для него receive_loop, и записывают в свою область памяти, обозначенную в коде названием master, разные pid. Я верю, что Эрланг имеет какие-то средства для защиты от этого, но из вашего кода они неочевидны.диск — хорошо, память — плохо
Для той задачи, для которой делают упомянутые вами "походы в базу" да. Именно затем их и делают. Там где не надо, просто не делают, есть кэш и локальные переменные. Кэш на отдельной ноде, который хранит данные в оперативной памяти и возвращает их по сетевому запросу, это примерно то же самое, что и процесс из примера в статье.
Медленнее, у́же бутылочное горлышко, в разы хуже и сложнее масштабируется, но — лучше.
По критерию консистентности данных да, лучше. Масштабируется тоже неплохо, запустили 10 контейнеров или 10 джобов в одном контейнере, и они работают себе параллельно. Поддержки языка для параллельности обычно нет, это да, но со способом сохранения данных это не связано.
cupraer Автор
17.05.2025 08:01«Не используйте одни транзакции, используйте другие.»
Нет, язык из коробки предоставляет транзакционное обращение к состоянию процесса, вы не можете получить там гонку.
А он сам магически будет писать то что мне нужно […]
EventLog
? — Магически, разумеется. В любом языке, причем. Это же контракт.У нас 2 ноды, которые обрабатывают веб-запросы, обе запросили товар 321, процесса для которого еще нет.
В данном случае
321
— это и есть идентификатор процесса. Создание процесса — синхронный вызов (если программист специально не извернулся). Поэтому первое обращение вернет{:ok, pid},
а второе —{:error, {:already_started, pid}}
. В тексте этого действительно нет, потому что текст не об этом.Кэш на отдельной ноде, который хранит данные в оперативной памяти и возвращает их по сетевому запросу, это примерно то же самое, что и процесс из примера в статье.
Нет. Потому что вы не можете просто разнести кэш по пяти нодам прозрачно для вызывающего кода, а где именно запущен процесс — вызывающему коду знать не нужно. Существуют distributed cache решения, но они очень накостыленные обычно, если нет прозрачного механизма отсылки сообщений (вызова методов) внутри кластера.
По критерию консистентности данных да, лучше.
Это утверждение основано на «с диском я работал, и мне нравится». Моё (обратное) основано на «я работал и с тем и с другим, и я так не думаю».
michael_v89
17.05.2025 08:01С намеренно реализованной системой транзакций, которая имеет защиту от сбоев в середине записи
транзакционное обращение к состоянию процессаКак я написал в самом начале, я говорил про транзакционную запись на диск для защиты от сбоев. Что означает 1) для одного процесса отсутствие ситуации, когда записалась только половина нового имени пользователя, а потом произошел сбой, и процесс после перезапуска возвращает в поле name половину старых данных и половину новых, и 2) для нескольких процессов отсутствие ситуации, когда при отправке статьи на модерацию надо создать новую запись ModerationRequest и поменять Article.state = ON_MODERAION, но произошел сбой, и состояние ModerationRequest сохранилось, а состояние Article нет. Бизнесу такие ситуации почему-то не нравятся, и он просит сделать так, чтобы их было как можно меньше.
В любом языке, причем. Это же контракт.
Я не знаю, что вы имеете в виду, в тех языках с которыми я работал, никакие логи при записи нового значения в переменную state магически сами не пишутся.
Потому что вы не можете просто разнести кэш
Обычно это и не надо. В других языках никто не загружает все 100 гигабайт базы в оперативную память.
Это утверждение основано на «с диском я работал, и мне нравится»
Нет, не на этом. А на понимании того, что произойдет в обоих случаях, насколько это нужно бизнесу, и сколько дополнительной работы это может мне создать. В том числе на ваших утверждениях вида "можно просто забить на потерявшиеся колбасы". В моих проектах бизнес этого не хочет, и достичь этого довольно просто.
cupraer Автор
17.05.2025 08:01Вы и близко не понимаете, что произойдет в случае без диска.
michael_v89
17.05.2025 08:01Как я и сказал, для этого мне достаточно ваши утверждений. Видимо придется все процитировать.
"Можно по камерам отследить все неоплаченные колбасы в зале (медленно и неэффективно, но надо — значит надо)".
"Можно договориться считать баланс после закрытия магазина".
"Можно, в конце концов, вспомнить о понятии «страховой случай» и просто забить на потерявшиеся колбасы".Вы сами сообщаете, что в вашем подходе это потребуется в определенных случаях. У меня как раз такие определенные случаи. Я не хочу делать всю эту работу, мне это не надо, поэтому я использую подход, где она не нужна. И это мы еще не говорили о том, что аналитики от бизнеса работают SQL-запросами с базой, и им нужны актуальные данные в базе, а не где-то в оперативной памяти приложения, куда они не имеют доступа.
cupraer Автор
17.05.2025 08:01Вы обо всех технологиях судите по словам случайного горлопана в интернете? Что ж, это многое объяснило бы.
cupraer Автор
17.05.2025 08:01Никогда не страдал излишней скромностью, впрочем, и тщеславия лишен напрочь.
В комментарии выше я просто указал на то, что специальным образом даже вынес в эпиграф этой заметки, — просто потому, что был абсолютно убежден в присутствии в комментариях людей такого сорта.
SolidSnack
17.05.2025 08:01Простите, а в вашем мире «БАЗА» — это такое магическое существо, волшебно сохраняющее всё навсегда, безотказно, даже в случае нетворк сплитов и отказов жесткого диска? Откуда у вас такая слепая вера в Великую Базу — и такое недоверие к процессам? Чем процесс хуже?
Это вера в репликацию данных
cupraer Автор
17.05.2025 08:01А процесс реплицировать — законом запрещено, или что? Кроме того, репликация бывает разная, и при упомянутом отказе ЖД — вас и с базой ждут гораздо более серьезные сложности, чем «восстановление из бэкапа».
SolidSnack
17.05.2025 08:01А зачем вы файлы сохраняете на жёсткий диск? У вас нету каких нибудь носителей с копией данных которые вы бы хотели сохранить?
Если честно это все спор ради спора, мне кажется, учитывая приведенный выше вами код, без каких либо комментариев даже.
Почему-то я открываю вики и оттуда мне понятно зачем акторы, а отсюда веет каким-то противопоставлениям ООП, опять же из комментариев. Я не могу вообще уловить суть как это сравнивать.
Акторная модель, будучи одной из самых математически элегантных концепций в Computer Science (наравне, пожалуй, с теорией категорий и property-based тестированием)
Вы сами говорите что это фундаментальные подходы в информатике и сравниваете их с ООП. Назовите хоть 1 книгу по CS где написано про ООП кроме упоминаний, и то сомневаюсь.
cupraer Автор
17.05.2025 08:01Почему-то я открываю вики и оттуда мне понятно зачем акторы […]
Это вы жалуетесь, или хвастаетесь?
Назовите хоть 1 книгу по CS где написано про ООП […]
Книжки я не читаю (кроме беллетристики). Диссертация подойдёт? Вот, одна из наиболее внятных.
SolidSnack
17.05.2025 08:01хвастаетесь
И что это за хвастовство такое?) Если я давно в ИТ сфере, почему мне не понимать его?)
Мне кажется вы и хотите похвастаться, только не могу понять чем
cupraer Автор
17.05.2025 08:01Если я давно в ИТ сфере, почему мне не понимать его?
Старость иногда приходит одна.
SolidSnack
17.05.2025 08:01Книжки я не читаю
А как вы познаете информацию? Например о сетях? О той же CS?
Если честно может стоит начать читать, там много полезного, ничего личного, но теперь эта статья висит в публичном доступе с 5 лайками и пометкой "Туториал", и каких "дошкольников" вы тут научите я не знаю
cupraer Автор
17.05.2025 08:01в публичном доступе с 5 лайками
О-о-о, измерение длины лайков, это прям выход в красное.
каких «дошкольников» вы тут научите я не знаю
Тех, которые считают целесообразным быть в мире акторами, а не объектами.
SolidSnack
17.05.2025 08:01Объективный человек более логичен, разве нет?)
О-о-о, измерение длины лайков, это прям выход в красное.
Лайки к тому что это поднимает авторитет перед читателем (для чего ещё они созданы? Отсеевать что от чего?) особенно далёким от всего этого читателям.
cupraer Автор
17.05.2025 08:01Нет, конечно. Объективность — синоним усредненности. Логика не имеет ничего общего с философской моделью мира, это чистая математика.
Например: мне нужна всего одна работа, и не сто тысяч, поэтому пока объективный тезис звучит как: «работу на стандартном стеке найти проще», — логичный вот так: «если я не хочу искать работу, ходить по собеседованиям и работать за гроши — надо научиться решать те задачи, которые остальные 99 999 соискателей решить не могут хотя бы в силу узости кругозора, и интересную работу начнут сами предлагать».
SolidSnack
17.05.2025 08:01Логика не имеет ничего общего с философской моделью мира, это чистая математика.
Математика это наука, а основа любой науки это философский подход к знаниям
(Мне тоже нужна работа, нафиг этих фреимворщиков и фронтендеров, на респекте;) )
michael_v89
17.05.2025 08:01его сначала нужно откуда-то (из базы) достать. Отсюда все эти N+1 проблемы
pid будет работоспособным, вне зависимости от того, на какой ноде процесс в результате запущенПроблема N+1 идет не из того, что надо что-то достать из базы. Потому что доставать из базы можно и без N+1. Она появляется из-за повторений относительно долгой операции в цикле.
В вашем подходе она тоже возможна. Если вы загрузили все товары из базы в виде таких процессов на одну ноду, заказ 123 на другую ноду, и выводите все его order items с названием товара, то у вас будет N запросов по сети с одной ноды на другую для получения названия товара по item.productId. Возможно это будет немного быстрее чем с базой, но проблема все равно присутствует. Чтобы ее избежать, надо специальным образом проектировать взаимодействие, чтобы выполнять несколько операций за одно обращение, так же как и с запросами в базу.cupraer Автор
17.05.2025 08:01надо специальным образом проектировать взаимодействие
Это зачем еще? Стейт процесса «заказ» — по сути то же самое, что документ из NoSQL — я прямо в него добавляю товары.
michael_v89
17.05.2025 08:01Чтобы отправлять к ноде с товарами не N сообщений, аналогичных по действию
SELECT name FROM product WHERE id = :id
, а одноSELECT name FROM product WHERE id IN (:idList)
. Иначе у вас такой же N+1, и работает он примерно столько же времени, потому что сеть работает одинаково что для базы, что для ноды Эрланга.cupraer Автор
17.05.2025 08:01Нет у меня никакого
:idList
, потому что его, для начала, хранить где-то надо, а это — лишнее. Предвосхищая вопрос про стейт на клиенте: у меня и джаваскрипта нет, мы всё давно на LiveView делаем.Нажалась кнопка «add to cart» — выслалось сообщение, стейт заказа обновился.
michael_v89
17.05.2025 08:01Нет у меня никакого :idList
Я именно про это и говорю. Нет idList, значит есть N запросов по сети к ноде с товарами от ноды, которая работает с заказом, и по этому критерию ваш подход ничем не лучше базы. Джаваскрипт тут ни при чем. Сart тоже ни при чем, я говорю про отображение уже созданного заказа.
cupraer Автор
17.05.2025 08:01michael_v89
17.05.2025 08:01я прямо в него добавляю товары
Во-первых, я говорю не про добавление товаров, а про отображение заказа позже.
Во-вторых, создание заказа из моего примера тоже можно описать выражением "прямо в него добавляю товары".
Видимо вы подразумеваете копирование всего состояния товара, включая название, а не только id. В случае с заказами это имеет смысл, поэтому мой пример не совсем удачный, но вы пытаетесь перевести разговор на частности и увильнуть от темы, поэтому придется привести другой пример.Вы загрузили всех Developer на одну ноду, потом выводите список задач на другой ноде, у каждой задачи надо вывести имя ответственного разработчика. Если человек поменял имя (например, девушка вышла замуж), должно выводиться текущее имя, если человек уволился, должен выводиться статус, что он уволился, поэтому копированием состояния при создании задачи вы это не решите. При выводе будет точно такая же проблема N+1. Аналогично список статей на Хабре. Пользователь поменял ник или аватарку, должно выводиться текущее состояние.
При создании заказа с вашим подходом она тоже возможна, если есть задача, чтобы "add to cart" поддерживал добавление нескольких товаров сразу. Аналогично несколько категорий в новости, хабы в статье на Хабре, и любые другие запросы на создание с множественными идентификаторами. Поэтому утверждение, что в вашем подходе ее нет потому что нет работы с базой, не соответствует действительности.
cupraer Автор
17.05.2025 08:01Я никогда не пытаюсь увильнуть от темы, это во-первых. Во-вторых, все ваши аргументы годятся для полного уничижения NoSQL баз. В-третьих, у вас реляционность головного мозга.
Я легко мог бы и тут показать, как такие задачи решаются без реляций, но сегодня воскресенье, а подаю я только по пятницам. Я не ставлю перед собой задачу научить заворачивать шурупы всех людей с молотками в руках.
cupraer Автор
17.05.2025 08:01Для протокола: при изменении фамилии я просто грохну процессы задач, супервизор их перезапустит и правильные актуальные данные подтянутся без моего участия.
Тот самый зирокод, матьиво.
michael_v89
17.05.2025 08:01Во-вторых, все ваши аргументы годятся для полного уничижения NoSQL баз.
Конечно. Поэтому их бум прошел и его повторение не намечается. Именно из-за проблем с реляционностью и транзакциями. В каких-то нишах они подходят и используются, как и любой инструмент.
я просто грохну процессы задач и актуальные данные подтянутся
Вот-вот, оказывается еще и грохать вручную что-то надо. А еще и сохранить перед этим, а то вдруг там менеджер только что большое описание добавил и ответственного поменял. А мне ничего искать и грохать не надо, именно для этого и придумали реляции и внешние ключи.
В-третьих, у вас реляционность головного мозга.
Нет, просто я знаю как они работают и могу оценить количество действий для поддержания консистентности данных с ними и без. Для моих языков и проектов с ними проще реализовывать нужные бизнес-требования.
cupraer Автор
17.05.2025 08:01их бум прошел и его повторение не намечается
А пацаны в курсе? А то вот каждый второй из предлагающих мне работу — либо на Кассандре, либо на Равене. ElasticSearch есть у 90%. Я не работаю на США по идеологиченским соображениям, но вот буквально на днях Блумберг хотел именно подружить эрланг с Кассандрой.
еще и сохранить перед этим
Нет, сохранить не надо.
могу оценить количество действий для поддержания консистентности данных с ними и без
Нет, без них вы не можете ничего оценить, что и наглядно демонстрируете в ветке выше. Вы критикуете Пастернака, которого не читали. Вы не понимаете бо́льшей половины того, что я говорю (а я использую лексикон для домохозяек, на таком уровне кругозора должно хватать даже упоротым хаскелистам).
michael_v89
17.05.2025 08:01ElasticSearch есть у 90%
"В каких-то нишах они подходят и используются, как и любой инструмент."
Вы не понимаете бо́льшей половины того, что я говорю
"В тексте этого действительно нет"
"Я легко мог бы и тут показать, как такие задачи решаются без реляций, но не буду"
Наверно потому что вы говорите не всё? И не способны оценить, как выглядят ваши слова для собеседника?а я использую лексикон для домохозяек
Может в этом и причина, что вас не понимают?
Вы критикуете Пастернака, которого не читали.
Мне достаточно того, что об этом сообщает тот, кто его читал. Вы говорите "надо по камерам отслеживать все неоплаченные колбасы, это медленно и неэффективно", и я вам верю.
dzmitry_sidarau
17.05.2025 08:01Тут, наверное, нужно пояснение для аудитории о видах процессов в OTP, деревьях супервизоров и let it crash. Иначе вы будто спорите о разном :)
cupraer Автор
17.05.2025 08:01Тут не требуется никаких пояснений: у человека в руках молоток, и он хочет им заколачивать всё вокруг, от шурупов до болтов на 42.
Про виды процессов и деревья супервизоров можно почитать буквально везде в интернете, я же не могу любой текст начинать с разжёвывания прописных истин для твердолобых. Кто захочет — ключевые слова для поиска все на месте :)
michael_v89
17.05.2025 08:01у человека в руках молоток, и он хочет им заколачивать всё вокруг
Я вам уже объяснил, что это не так. Но видимо вы в диалоге предпочитаете разговаривать сами с собой, а не с собеседником.
michael_v89
17.05.2025 08:01"Нет, просто я знаю как они работают и могу оценить количество действий для поддержания консистентности данных с ними и без. Для моих языков и проектов с ними проще реализовывать нужные бизнес-требования."
Видимо надо еще подробнее объяснить.
1. Языки в проектах, где я работаю, и вообще многие языки, не поддерживают hot reload на продакшене. Поэтому любой деплой означает полную выгрузку процесса из памяти со всеми данными. Деплой происходит несколько раз в неделю.
2. Бизнес не хочет терять данные несколько раз в неделю, и вообще ни разу. Особенно связанные с финансами. И не хочет платить за проверку логов и исправление при их потере.
3. Бизнес хочет предоставлять аналитикам доступ к базе, где им нужны актуальные данные на текущий момент, которые они получают с помощью произвольных SQL-запросов.
4. Бизнес хочет иметь для себя админку, где он может задать фильтр по статусу заказа и получить актуальный список заказов в этом статусе. Бизнес хочет иметь актуальные данные когда ему нужно, а не в конце дня.И всё это решается одним простым вызовом "save()" после внесения изменений в переменную (и локом на id при загрузке). Мне не надо медленно и неэффективно анализировать логи, не надо писать отдельный расчет баланса после закрытия магазина, не надо объяснять бизнесу, что он должен забить на некорректные данные, и не надо решать проблемы с бизнес-логикой в джобах. Производительности хватает для довольно больших нагрузок, а проблемы с масштабированием если и возникают, то на масштабах Тиндера и Амазона, до которых развивается не каждый бизнес. Именно поэтому я выбираю такой подход - потому что для этих задач и в этих условиях он подходит лучше. Да, ваш подход производительнее ценой меньшей консистентности данных и дополнительной работы программиста, но повышать производительность мне нет необходимости, поэтому преимущества этого подхода в моей работе ничего не изменят, и останутся только недостатки.
cupraer Автор
17.05.2025 08:01ваш подход производительнее ценой меньшей консистентности данных и дополнительной работы программиста
Я же сказал уже триста раз, вы кроме молотка ничего не понимаете, но пытаетесь судить на основе моих каких-то обрывочных комментариев, причем пропущенных через призму вашего непонимания. И это выглядит довольно грустно.
Работы программиста меньше, консистентность данных выше, а бизнес получает всё вами перечисленное и даже больше.
К контексту дискуссии это не имело отношения, но сейчас могу сказать. Да, у нас есть база. Да, мы используем её для сохранения консистентных состояний. Но мы не ходим в неё на каждый чих, не перекладываем ответственность за понимание того, что такое «консистентный» на магическую палочку зубной феи под названием «транзакция», не блокируем ничего и никогда и тем самым получаем максимальную скорость отклика базы, измеряемую миллисекундами в наихудшем случае. При этом, если база по-настоящему навернётся (а я попадал в такую ситуацию дважды, причем в первый раз — AWS полностью потерял данные за последние почти пятнадцать секунд — и именно это побудило вообще задуматься о том, что база — не великая и нерушимая скала, поэтому делать её краеугольной — не сто́ит), — то мы ничегошеньки не потеряем.
Любой отказ внешнего сервиса, включая хранилище — штатная ситуация, данные не потеряются не потому, что программист написал тонну защитного кода, а потому что акторная модель так умеет из коробки.
И нет, мы не пользуемся hot reload (который невозможен в докере из-за того, что его писали дегенераты).
michael_v89
17.05.2025 08:01Спор в этой ветке идет о проблеме N+1 и утверждении, что она появляется из-за чтения из базы, а если читать из процесса на другой ноде, то ее не будет. Это ложь. Если мы привязываем процессы к id сущностей, значит хотим в каких-то сценариях получать информацию, имея только id сущности. Единственный способ ее избежать это загружать информацию сразу по нескольким id в одном сетевом запросе от одной ноды к другой. Никакие деревья супервизоров на этих нодах не изменят механизм работы сети.
cupraer Автор
17.05.2025 08:01Вы меня великодушно простите, но если вы самозабвенно дискутируете с каким-то вырванным из контекста утверждением, — это не значит, что все остальные вовлеченные против воли в этот бессмысленный монолог «ведут спор».
Если бы я знал, что спор идёт про
N+1
— я бы сразу сказал следующее: архитектура любого приложения с реляционной базой провоцирует наN+1
и чтобы её избежать — надо про неё знать, архитектура любого приложения на акторной модели — во время написания первой строки кода сN+1
— буквально закричит: «Ты всё делаешь неправильно! Опомнись! Перепиши нормально!».Но дело даже не в этом, а в том, что
N+1
страшна не сама по себе: сто запросов база выполнит не настолько медленнее одного, чтобы это оказалось заметно пользователю. Куча приложений, причем достаточно нагруженных, жило годами сN+1
и бизнес особо не страдал.N+1
страшна тем (ввиду самого наличия базы как узкого места), что приближает ситуацию «база не справляется с нагрузкой». Пилюля против этой проблемы одна — вертикальное масштабирование, которое быстро упирается в потолок. Все остальные решения — наподобие консенсуса — настолько сложны, что всегда проще переписать проект с нуля.И вот когда база начинает мигать красными метриками (которые буквально были упомянуты через запятую после
N+1
, но вы про них элегантно забыли, да еще и набрались наглости меня упрекать в увиливании от темы) — условный вы пойдёте искать себе новую работу, и позовут условного меня.Been, как говорится, there.
michael_v89
17.05.2025 08:01Если бы я знал, что спор идёт про N+1
Цитаты и возражения про N+1 написаны в самом первом комментарии этой ветки. Этого достаточно, чтобы знать.
архитектура любого приложения на акторной модели — во время написания первой строки кода с N+1 буквально закричит
Код, который вы показали в статье, ничего такого не кричит. А кричит прямо противоположное - чтобы получить значение поля из сущности, надо к ней обратиться по одному id.
N+1 страшна тем, что приближает ситуацию «база не справляется с нагрузкой». Пилюля против этой проблемы одна — вертикальное масштабирование
Нет, решение проблемы большой нагрузки из-за N+1 решается путем убирания ситуаций с N+1. Это довольно несложно.
да еще и набрались наглости меня упрекать в увиливании от темы
"Неувиливание" было бы сказать "Конкретно с заказами имеет смысл копировать состояние товара на момент заказа, но в целом вы правы, N+1 в некоторых сценариях возможна и в моем подходе". А вы вместо этого сказали "А я ваш пример по-другому сделаю, бе-бе-бе", а про основную тему ничего не сказали, хотя пример был нужен только для демонстрации этой темы. Единственная причина существования N+1 это запрос информации с удаленного сервера по единичному id несколько раз. Если у вас есть такая операция, значит эта проблема возможна, и это не зависит от того, база у вас на этом сервере или не база, а чтобы ее избежать, надо специальным образом проектировать взаимодействие. Непризнание этого это и есть то, что я назвал увиливанием.
cupraer Автор
17.05.2025 08:01Увиливание — это продолжать цепляться за
N+1
, начисто игнорируя все остальные тезисы.N+1
в акторной модели — не критично. А с базой, как единым местом отказа — критично. Вот и всё.Столкнётесь — вспомните этот разговор и пожалеете, что пытались мне что-то доказать, вместо того, чтобы набраться мудрости и поблагодарить. Но это вряд ли.
Dhwtj
ООП вульгарис имеет более низкий порог вхождения чем ФП или акторная модель. Особенно если не погружаться глубоко в эти ужасы.
Вопрос: почему же так важен низкий порог вхождения? Программист что одноразовый? Разве средний стаж программиста меньше года? Особенно сейчас, когда изучить библиотеки и фреймворки проще с LLM чем лет 5-10 назад когда опыт программиста приравнивался к задрачиванию знаний о фреймворках. (По ощущениям)
Бизнесу нужны стандартные и дешёвые юниты, увы!
Но не в интересах программиста такая узкая и дешёвая специализация.
Jijiki
тогда пусть llm сделает обратную(inverse) матрицу 4х4 сделает ну код будет верный?
Dhwtj
Jijiki
случай с обраткой у меня какая-то модель начала глючить, и рядом было открыто полное руководство как это сделать, а модель предлагала мне усовершенствованные решения, когда я его просил не делать так, а всё развернуть, на что модель мне сказала, что да тут вот так и дала мне упакованное в алгоритм решение, в итоге пришлось по наитию погружаться, после чего где-то после еще 0-5 промпта я дал ему понять, что Минор для 4х4 он и детерминант он считает не правильно, он делал вид что понимает меня, но ответ был не верный, и потом он сам догадался что надо сверяться с калькулятором куда потом и посылал меня, НО замечу ошибку в моём решении он так и не указал, мне её пришлось самому искать, другая модель посильнее к примеру дала код на анимацию чтобы палочка 1-штука двигалась вверх вниз - только этот пример скомпилировался, расширенный пример мы так и не смогли запустить с ИИ он в одной ошибке ушел в дебри, может эти задачи генерируют большое количество токенов или чего-то там нужного, в одном случае рутина проверить повторяемые данные, в другом просто задача громоздкая
cupraer Автор
Я привел пример сравнения прямо в тексте и продолжаю пребывать в уверенности, что низкий порог входа — это сказки.
Dhwtj
GenServer не похож на замену класса в ООП. Скорее, на замену микросервиса. Или как?
Если замена каждого класса то будет что-то непонятное с производительностью
cupraer Автор
GenServer
— класс, процесс — инстанс класса.Угу, прям беды, вон спросите у команды вотсапа (или у меня). Очень всё понятно, легковесных процессов можно запустить на одной ноде примерно около миллиона (и больше, если прям надо).