Здравствуйте, ·, Вы писали:
·>Здравствуйте, Sinclair, Вы писали:
S>>>>Там ничего неясного нет — просто добавляется ещё один предикат во where, а заказ распадается на reserved и residual. S>>·>Т.е. в одной транзакции будут roundtrips между субд и приложением для каждого товара? S>>Нет, это всё можно записать двумя стейтментами на весь заказ, вообще без раундтрипов. ·>Интересно, как это выглядит?
Примерно так:
BEGIN TRANSACTION;
WITH-- 1) Лочим заказ и одновременно проверяем, что он в draft
ord AS (
SELECT o.orderId
FROM orders o
WHERE o.orderId = @orderId
AND o.state = 'draft'
FOR UPDATE
),
-- Позиции заказа (orderId уже фиксируем через join на ord)
req AS (
SELECT oi.productId, oi.quantity
FROM orderItem oi
JOIN ord ON ord.orderId = oi.orderId
),
-- 2) Лочим строки stock в фиксированном порядке (anti-deadlock)
locked AS (
SELECT s.productId, s.available
FROM stock s
JOIN req r ON r.productId = s.productId
ORDER BY s.productId
FOR UPDATE
),
-- 2b) Проверка all-or-nothing:
-- - заказ был draft (ord существует)
-- - для каждой позиции есть строка stock
-- - available хватает для каждой позиции
chk AS (
SELECT
(SELECT COUNT(*) FROM ord) AS ord_cnt,
(SELECT COUNT(*) FROM req) AS need_cnt,
(SELECT COUNT(*) FROM locked) AS locked_cnt,
(SELECT COUNT(*)
FROM locked l
JOIN req r USING (productId)
WHERE l.available >= r.quantity
) AS ok_cnt
),
-- 3) Обновляем stock только если ВСЁ ОК
upd_stock AS (
UPDATE stock s
SET available = s.available - r.quantity,
reserved = s.reserved + r.quantity
FROM req r, chk
WHERE s.productId = r.productId
AND chk.ord_cnt = 1
AND chk.need_cnt > 0
AND chk.need_cnt = chk.locked_cnt
AND chk.need_cnt = chk.ok_cnt
RETURNING 1
),
-- 4) Меняем статус заказа только если stock реально обновился
upd_order AS (
UPDATE orders o
SET state = 'reserved'
WHERE o.orderId = @orderId
AND EXISTS (SELECT 1 FROM upd_stock)
RETURNING o.orderId, o.state
)
SELECT * FROM upd_order;
COMMIT;
·>И что если кто-то закинет заказ на тысячу позиций?
Будет попытка зарезервировать заказ на тысячу позиций.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[43]: Помогите правильно спроектировать микросервисное при
Здравствуйте, Sinclair, Вы писали:
S>>>>>Там ничего неясного нет — просто добавляется ещё один предикат во where, а заказ распадается на reserved и residual. S>>>·>Т.е. в одной транзакции будут roundtrips между субд и приложением для каждого товара? S>>>Нет, это всё можно записать двумя стейтментами на весь заказ, вообще без раундтрипов. S>·>Интересно, как это выглядит? S>Примерно так:
Ок. Выглядит совсем не так, как в предыдущем примере И вроде нету "заказ распадается на reserved и residual".
S>ORDER BY s.productId
S>FOR UPDATE
S> UPDATE orders o
S> RETURNING o.orderId, o.state
Понятно, я безнадёжно отстал от современного sql. Последний раз с sql серьёзно работал лет 10 назад...
S>·>И что если кто-то закинет заказ на тысячу позиций? S>Будет попытка зарезервировать заказ на тысячу позиций.
Ну вроде речь о МСА шла, а у тебя тут orders и stock в одной транзакции. В МСА же будет некий условный json на резервацию. И тут начнутся проблемы с передачей массивных объектов целиком. Скажем, в kafka размер сообщения жестко ограничивается.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>Ок. Выглядит совсем не так, как в предыдущем примере И вроде нету "заказ распадается на reserved и residual".
Простите. Это я что-то начал подтупливать — тяжёлая неделя.
Это у нас код all or nothing и в монолите. Распиливание пополам выглядит в деталях по-другому, но принцип тот же. ·>Понятно, я безнадёжно отстал от современного sql. Последний раз с sql серьёзно работал лет 10 назад...
Это postgres-specific расширение SQL. У MS есть аналог, но с другим синтаксисом.
·>Ну вроде речь о МСА шла, а у тебя тут orders и stock в одной транзакции. В МСА же будет некий условный json на резервацию. И тут начнутся проблемы с передачей массивных объектов целиком. Скажем, в kafka размер сообщения жестко ограничивается.
А, если в MCA то всё ещё проще. Просто отдаём JSON и получаем обратно не такой же объект, как был — и уже при обработке респонса пилим свой order на reserved часть и всё остальное.
Опять же, это распиливание мы можем сделать безо всяких раундтрипов, передав в СУБД список успешно зарезервированных позиций в удобном для неё виде.
Размеры сообщений — это какая-то надуманная проблема. Если кафка не может передать сообщение нужного размера, то нужно выкинуть кафку. Описанный мной способ не полагается на внешние exactly-once системы доставки.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[45]: Помогите правильно спроектировать микросервисное при
Здравствуйте, Sinclair, Вы писали:
S>·>Ок. Выглядит совсем не так, как в предыдущем примере И вроде нету "заказ распадается на reserved и residual". S>Простите. Это я что-то начал подтупливать — тяжёлая неделя. S>Это у нас код all or nothing и в монолите. Распиливание пополам выглядит в деталях по-другому, но принцип тот же.
Да, ты похоже, не на тот вопрос ответил
Я тут речь завёл о том, чтобы распилить транзакцию произвольного размера на множество мелких транзакций фиксированного размера. Тут: https://rsdn.org/forum/design/9057463
S>·>Ну вроде речь о МСА шла, а у тебя тут orders и stock в одной транзакции. В МСА же будет некий условный json на резервацию. И тут начнутся проблемы с передачей массивных объектов целиком. Скажем, в kafka размер сообщения жестко ограничивается. S>А, если в MCA то всё ещё проще. Просто отдаём JSON и получаем обратно не такой же объект, как был — и уже при обработке респонса пилим свой order на reserved часть и всё остальное.
Вопрос о том, как сервис резервации будет взаимодейтсвовать с субд. Получил сервис резервации гигантский объект и пока его жуёт — все остальные должны ждать на блокировках.
Представь себе, есть куча заказов одной популярной сковородки. И большинство заказов из одной позиции именно на эту сковородку. И тут кто-то запулливает гигантский ордер, среди позиций есть эта сковородка. Придётся ждать всем!
Да и вообще неясно в чём преимущество заталкивать всё в одну транзакцию.
S>Опять же, это распиливание мы можем сделать безо всяких раундтрипов, передав в СУБД список успешно зарезервированных позиций в удобном для неё виде.
Как без раундрипов резервировать с успехом/неуспехом каждую позицию?
S>Размеры сообщений — это какая-то надуманная проблема. Если кафка не может передать сообщение нужного размера, то нужно выкинуть кафку.
Тут дело принципиальное — чтобы система была responsive, надо гонять мелкие сообщения. Пока кафка передаёт гигантское сообщение — другим сообщениям придётся ждать своей очереди. Ждут все! А если большое сообщение разбить на мелкие части, то ждать будет только тот, кому надо много.
S> Описанный мной способ не полагается на внешние exactly-once системы доставки.
Тут нужен at-least-once. Да, и при redelivery тоже придётся гигантское сообщение дублировать.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[46]: Помогите правильно спроектировать микросервисное при
Здравствуйте, ·, Вы писали:
S>>А, если в MCA то всё ещё проще. Просто отдаём JSON и получаем обратно не такой же объект, как был — и уже при обработке респонса пилим свой order на reserved часть и всё остальное. ·>Вопрос о том, как сервис резервации будет взаимодейтсвовать с субд. Получил сервис резервации гигантский объект и пока его жуёт — все остальные должны ждать на блокировках.
Представления о гигантскости объектов сильно искажены идиотскими реализациями hibernate-based приложений.
Вот в кровавом рич DDD это действительно может стать проблемой: мы начали транзакцию, в которой мееееееееееееееееееедленно такаем складские остатки из базы в апп-сервер, потом их обновляем и меееееееееееееееееееееееееедленно записываем обратно.
Когда код пишется прямыми запросами к СУБД, заказ в тыщу позиций обработается примерно мгновенно. Особенно если данные лежат на современном SSD диске.
Я уже неоднократно сталкивался с тем, что у людей сдвинута интуиция относительно быстродействия СУБД на современном железе. Лет несколько тому кто-то меня уверял, что реляционка захлебнётся на обходе графа из миллиона элементов. Оказалось — нет, даже у лаптопа пред-предыдущего поколения вполне хватает дури обрабатывать такое незаметно для пользователя.
·>Представь себе, есть куча заказов одной популярной сковородки. И большинство заказов из одной позиции именно на эту сковородку. И тут кто-то запулливает гигантский ордер, среди позиций есть эта сковородка. Придётся ждать всем!
О да. Двадцать миллисекунд придётся подождать. ·>Да и вообще неясно в чём преимущество заталкивать всё в одну транзакцию.
Преимущества транзакций — в согласованности. Мы вот взяли и придумали какие-то инварианты, отражающие ограничения бизнеса. Транзакции — единственный способ обеспечить распределённые инварианты (то есть несводимые к ограничениям на 1 элемент данных. Проверки типа "возраст не может быть отрицательным" можно сделать и без транзакций).
Вот есть, к примеру, инвариант "статус относится ко всему заказу — черновик, зарезервирован, оплачен, отгружен, получен и т.п." На этот инвариант полагается масса кода, включая отчёты.
Если мы отменяем этот инвариант, придётся переделывать примерно всё.
S>>Опять же, это распиливание мы можем сделать безо всяких раундтрипов, передав в СУБД список успешно зарезервированных позиций в удобном для неё виде. ·>Как без раундрипов резервировать с успехом/неуспехом каждую позицию?
Язык SQL много чем плох, а вот прекрасен он батч-операциями. Можно отдать ему стейтмент, который поменяет сразу много позиций.
В моём предыдущем примере позиции либо менялись, либо не менялись все разом; небольшое изменение предиката приведёт к тому, что позиции будут резервироваться частично.
·>Тут дело принципиальное — чтобы система была responsive, надо гонять мелкие сообщения. Пока кафка передаёт гигантское сообщение — другим сообщениям придётся ждать своей очереди. Ждут все! А если большое сообщение разбить на мелкие части, то ждать будет только тот, кому надо много.
Повторюсь: если Кафка становится узким местом, нужно выкинуть кафку. В рассматриваемом мной примере никакой Кафки нет, и ждут только те, кто ждёт. Ну и, опять же, в современных сетях всё происходит достаточно шустро. Главное — минимизировать количество раунд-трипов. ·>Тут нужен at-least-once. Да, и при redelivery тоже придётся гигантское сообщение дублировать.
Редчайший случай, заказ на тысячу позиций. Какого характерного размера это "гигантское" сообщение? В непожатом JSON оно будет занимать полсотни килобайт. Семечки.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[47]: Помогите правильно спроектировать микросервисное при
Здравствуйте, Sinclair, Вы писали:
S>·>Вопрос о том, как сервис резервации будет взаимодейтсвовать с субд. Получил сервис резервации гигантский объект и пока его жуёт — все остальные должны ждать на блокировках. S>Представления о гигантскости объектов сильно искажены идиотскими реализациями hibernate-based приложений. S>Вот в кровавом рич DDD это действительно может стать проблемой: мы начали транзакцию, в которой мееееееееееееееееееедленно такаем складские остатки из базы в апп-сервер, потом их обновляем и меееееееееееееееееееееееееедленно записываем обратно. S>Когда код пишется прямыми запросами к СУБД, заказ в тыщу позиций обработается примерно мгновенно. Особенно если данные лежат на современном SSD диске. S>Я уже неоднократно сталкивался с тем, что у людей сдвинута интуиция относительно быстродействия СУБД на современном железе. Лет несколько тому кто-то меня уверял, что реляционка захлебнётся на обходе графа из миллиона элементов. Оказалось — нет, даже у лаптопа пред-предыдущего поколения вполне хватает дури обрабатывать такое незаметно для пользователя.
Возможно, что мои знания устарели...
S>·>Представь себе, есть куча заказов одной популярной сковородки. И большинство заказов из одной позиции именно на эту сковородку. И тут кто-то запулливает гигантский ордер, среди позиций есть эта сковородка. Придётся ждать всем! S>О да. Двадцать миллисекунд придётся подождать.
Для FX-трейдинга — задержка 20мс — это production issue. Типичная latency — 0.3мс.
S>·>Да и вообще неясно в чём преимущество заталкивать всё в одну транзакцию. S>Преимущества транзакций — в согласованности. Мы вот взяли и придумали какие-то инварианты, отражающие ограничения бизнеса. Транзакции — единственный способ обеспечить распределённые инварианты (то есть несводимые к ограничениям на 1 элемент данных. Проверки типа "возраст не может быть отрицательным" можно сделать и без транзакций). S>Вот есть, к примеру, инвариант "статус относится ко всему заказу — черновик, зарезервирован, оплачен, отгружен, получен и т.п." На этот инвариант полагается масса кода, включая отчёты. S>Если мы отменяем этот инвариант, придётся переделывать примерно всё.
Так ведь у нас уже есть бизнес-сущность "частично зарезервированный заказ". Поэтому уже неважно — на 99.9% или на 0.1% зарезервированный. Алгоритмы будут те же.
S>>>Опять же, это распиливание мы можем сделать безо всяких раундтрипов, передав в СУБД список успешно зарезервированных позиций в удобном для неё виде. S>·>Как без раундрипов резервировать с успехом/неуспехом каждую позицию? S>Язык SQL много чем плох, а вот прекрасен он батч-операциями. Можно отдать ему стейтмент, который поменяет сразу много позиций. S>В моём предыдущем примере позиции либо менялись, либо не менялись все разом; небольшое изменение предиката приведёт к тому, что позиции будут резервироваться частично.
Батчинг — это уже ортогонально бизнес-требованиям, это нефункциональное требование. Можно батчами обрабатывать входные заявки на резервацию. Они мелкие, зафигачивай сразу кусками по 100 штук (или сколько намерим оптимально, параметр в конфиге). Опять же — суть в том, что размер транзакции фиксированный, а не зависит от данных идущих от юзеров. Это, кстати, сразу бесплатно даёт более эффективную обработку мелких заказов. Ломанулась куча народа покупать по скидке одну сковородку — фигня, будут обработаны пачками максимально быстро.
S>·>Тут дело принципиальное — чтобы система была responsive, надо гонять мелкие сообщения. Пока кафка передаёт гигантское сообщение — другим сообщениям придётся ждать своей очереди. Ждут все! А если большое сообщение разбить на мелкие части, то ждать будет только тот, кому надо много. S>Повторюсь: если Кафка становится узким местом, нужно выкинуть кафку. В рассматриваемом мной примере никакой Кафки нет, и ждут только те, кто ждёт. Ну и, опять же, в современных сетях всё происходит достаточно шустро. Главное — минимизировать количество раунд-трипов.
Это скорее про поточную, конвеерную обработку. Когда идут потоки сообщений туда-сюда, друг друга не мешая, с минимальными блокировками.
S>·>Тут нужен at-least-once. Да, и при redelivery тоже придётся гигантское сообщение дублировать. S>Редчайший случай, заказ на тысячу позиций. Какого характерного размера это "гигантское" сообщение? В непожатом JSON оно будет занимать полсотни килобайт. Семечки.
В высокопроизводительных системах — порядка сотен байт.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
S>>О да. Двадцать миллисекунд придётся подождать. ·>Для FX-трейдинга — задержка 20мс — это production issue. Типичная latency — 0.3мс.
К счастью, FX-трейдинг — нишевая штука. Нафиг не нужен никому, кроме спекулянтов.
Поэтому рассматривать его требования как основу архитектуры типичного бизнес-приложения не нужно.
S>>Если мы отменяем этот инвариант, придётся переделывать примерно всё. ·>Так ведь у нас уже есть бизнес-сущность "частично зарезервированный заказ". Поэтому уже неважно — на 99.9% или на 0.1% зарезервированный. Алгоритмы будут те же.
Не факт. У нас рассуждения дошли до того, что мы можем разделить заказ на два заказа, одновременно сохраняя базовый инвариант, и обходя ограничения строго-атомарного резервирования.
·>Батчинг — это уже ортогонально бизнес-требованиям, это нефункциональное требование. Можно батчами обрабатывать входные заявки на резервацию.
Нет, вы не поняли. Батчинг тут не про несколько заявок, а про то, что одним стейтментом можно поменять произвольное количество "строк".
·>Это скорее про поточную, конвеерную обработку. Когда идут потоки сообщений туда-сюда, друг друга не мешая, с минимальными блокировками.
Я не умею работать с потоками сообщений. Что такое state мне понятно; вместе со всеми производными понятиями — когерентность, распространение, согласованность, несогласованность.
Что можно сказать про систему на потоках сообщений — да хз. Какие-то сообщения куда-то едут. Соответствует ли такая система каким-нибудь требованиям? Может и соответствует. Как это доказать — непонятно.
S>>Редчайший случай, заказ на тысячу позиций. Какого характерного размера это "гигантское" сообщение? В непожатом JSON оно будет занимать полсотни килобайт. Семечки. ·>В высокопроизводительных системах — порядка сотен байт.
Вы говорите о системах, в которых производительность возведена в кумиры, и ей в жертву приносят всё, включая здравый смысл.
Нет, я не против — мне самому очень нравится архитектура LMAX Disruptor. Но не как задача, которую решают пацаны, а как способ посмотреть на задачу под другим углом.
А в традиционном бизнесе нет нужды опередить соперника на долю наносекунды. Зато важно, чтобы инварианты не разъехались.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[49]: Помогите правильно спроектировать микросервисное при
Здравствуйте, Sinclair, Вы писали:
S>>>О да. Двадцать миллисекунд придётся подождать. S>·>Для FX-трейдинга — задержка 20мс — это production issue. Типичная latency — 0.3мс. S>К счастью, FX-трейдинг — нишевая штука. Нафиг не нужен никому, кроме спекулянтов. S>Поэтому рассматривать его требования как основу архитектуры типичного бизнес-приложения не нужно.
Так ведь по большому счёту разница невелика с т.з. усилий на разработку. А перформанс, он и в африке перформанс.
S>>>Если мы отменяем этот инвариант, придётся переделывать примерно всё. S>·>Так ведь у нас уже есть бизнес-сущность "частично зарезервированный заказ". Поэтому уже неважно — на 99.9% или на 0.1% зарезервированный. Алгоритмы будут те же. S>Не факт. У нас рассуждения дошли до того, что мы можем разделить заказ на два заказа, одновременно сохраняя базовый инвариант, и обходя ограничения строго-атомарного резервирования.
Не понял, что именно "не факт".
S>·>Батчинг — это уже ортогонально бизнес-требованиям, это нефункциональное требование. Можно батчами обрабатывать входные заявки на резервацию. S>Нет, вы не поняли. Батчинг тут не про несколько заявок, а про то, что одним стейтментом можно поменять произвольное количество "строк".
Это я плохо объяснил. Батчинг — это не требование бизнеса, а механизм повышения перформанса. Это когда запрос в субд на резервацию одной строки и ста строк занимает примерно одно и то же время, т.к. бОльшая часть времени уходит на всякие накладные расходы взаимодействия сервиса резервации и субд.
Следовательно, выгоднее отправлять стейтменты всегда по 100 штук, вне зависимости от того, разные это заказы или один большой.
Т.е. 1000 заказов с 1й позицией и 1 заказ на 1000 позиций потребует 10 батчей с запросами в субд и будет выполняться одинаковое время.
Т.е. вначале разеделяем заказы на мелкие сообщения резервации, сообщения (возможно батчами) засылаем в очередь. Эта очередь потом разгребается (возможно батчами) и ответы отсылаются таким же способом. Ответы резервации аггрерируются в суммарный статус заказа.
S>·>Это скорее про поточную, конвеерную обработку. Когда идут потоки сообщений туда-сюда, друг друга не мешая, с минимальными блокировками. S>Я не умею работать с потоками сообщений. Что такое state мне понятно; вместе со всеми производными понятиями — когерентность, распространение, согласованность, несогласованность. S>Что можно сказать про систему на потоках сообщений — да хз. Какие-то сообщения куда-то едут. Соответствует ли такая система каким-нибудь требованиям? Может и соответствует. Как это доказать — непонятно.
Что-то вроде многопоточки, асинхронное программирование?.. Акторы?..
S>>>Редчайший случай, заказ на тысячу позиций. Какого характерного размера это "гигантское" сообщение? В непожатом JSON оно будет занимать полсотни килобайт. Семечки. S>·>В высокопроизводительных системах — порядка сотен байт. S>Вы говорите о системах, в которых производительность возведена в кумиры, и ей в жертву приносят всё, включая здравый смысл. S>Нет, я не против — мне самому очень нравится архитектура LMAX Disruptor. Но не как задача, которую решают пацаны, а как способ посмотреть на задачу под другим углом.
Это по сути как реальность работает на низком уровне. REST-запрос — это два коррелированных сообщения запрос-ответ. А если не ограничиваться этим, то можно слать сообщения куда-то, и как-то реагировать на ответы откуда-то.
S>А в традиционном бизнесе нет нужды опередить соперника на долю наносекунды. Зато важно, чтобы инварианты не разъехались.
Ну в трейдинге тоже ничего разъезжаться не должно. Если разъедится — дело дрянь и серьёзные убытки.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[50]: Помогите правильно спроектировать микросервисное при
Здравствуйте, ·, Вы писали:
·>Так ведь по большому счёту разница невелика с т.з. усилий на разработку. А перформанс, он и в африке перформанс.
Разработка — она и в африке разработка. А вот проектирование...
·>Не понял, что именно "не факт".
Не факт, что мы смирились с заказами в "смешанном статусе" как с первоклассным состоянием системы.
·>Это я плохо объяснил. Батчинг — это не требование бизнеса, а механизм повышения перформанса. Это когда запрос в субд на резервацию одной строки и ста строк занимает примерно одно и то же время, т.к. бОльшая часть времени уходит на всякие накладные расходы взаимодействия сервиса резервации и субд.
Про этот батчинг я ничего не говорил. Я говорил про семантику объединения операций в ACID-группу и техническую реализацию такой семантики.
·>Следовательно, выгоднее отправлять стейтменты всегда по 100 штук, вне зависимости от того, разные это заказы или один большой.
Не совсем. Если вы попробуете сделать 1 коммит на пачку заказов, то в случае неудачи откатится вся транзакция, включая и те заказы, которым всего хватает.
·>Т.е. вначале разеделяем заказы на мелкие сообщения резервации, сообщения (возможно батчами) засылаем в очередь. Эта очередь потом разгребается (возможно батчами) и ответы отсылаются таким же способом. Ответы резервации аггрерируются в суммарный статус заказа.
Увеличиваем латентность, уменьшаем throughput.
·>Что-то вроде многопоточки, асинхронное программирование?.. Акторы?..
Ага. Вот мы сейчас изобретаем некоторый способ проектировать подобные системы так, чтобы потенциальные лайвлоки ловить не в продакшне, а в дизайн-тайме.
Что-то мне подсказывает, что у вас такого способа нет
·>Это по сути как реальность работает на низком уровне. REST-запрос — это два коррелированных сообщения запрос-ответ. А если не ограничиваться этим, то можно слать сообщения куда-то, и как-то реагировать на ответы откуда-то.
Ну так как только мы перестаём "ограничиваться", так сразу теряем всю привычную REST-семантику с её гарантиями. ·>Ну в трейдинге тоже ничего разъезжаться не должно. Если разъедится — дело дрянь и серьёзные убытки.
В трейдинге инварианты значительно более простые.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[50]: Помогите правильно спроектировать микросервисное при
Здравствуйте, ·, Вы писали:
·>Здравствуйте, Sinclair, Вы писали:
S>>>>О да. Двадцать миллисекунд придётся подождать. S>>·>Для FX-трейдинга — задержка 20мс — это production issue. Типичная latency — 0.3мс. S>>К счастью, FX-трейдинг — нишевая штука. Нафиг не нужен никому, кроме спекулянтов. S>>Поэтому рассматривать его требования как основу архитектуры типичного бизнес-приложения не нужно. ·>Так ведь по большому счёту разница невелика с т.з. усилий на разработку. А перформанс, он и в африке перформанс.
Разница огромная.
В бизнес-приложениях:
— ключевую роль играет стоимость разработки. Никому не нужно бизнес-приложение, которое стоит дороже самого бизнеса, какое бы крутое это приложение не было. Это линейный фактор.
— не надо соревноваться по скорости с другими системами. Поэтому перформанс в них — гигиенический фактор. Достигая определенного уровня он перестает давать ценность. Зато стоимость каждого следующего улучшения перформанса растет геометрически. Если разницу между 100мс и 20мс никто глазами не заметит, то нет смысла делать даже 20 мс.
— высокие требования к консистентности данных. потерять изменения в бизнес-приложении можно один максимум раз, после второго раза будет уже другой исполнитель. Это must-фактор. Нельзя иметь надежность ниже определенного уровня, в сценариях, которые встречаются на практике.
В трейдинге другие факторы:
— перформанс это must фактор, так как если ты работаешь медленее конкурентов, то ты просто не достигаешь целей.
— консистентность вообще можно не учитывать, так как все денные в программе уже устарели
— стоимость разработки программы на фоне размера счета — погрешность округления
Ну и количество сценариев в бизнес-приложении примерно на порядок или на два меньше, чем в трейдинге. В трейинге по сути один сценарий — приход информации от биржи, а в результате надо выдать пачку ордеров
Re[51]: Помогите правильно спроектировать микросервисное при
Здравствуйте, Sinclair, Вы писали:
S>·>Так ведь по большому счёту разница невелика с т.з. усилий на разработку. А перформанс, он и в африке перформанс. S>Разработка — она и в африке разработка. А вот проектирование...
... часть разработки.
S>·>Не понял, что именно "не факт". S>Не факт, что мы смирились с заказами в "смешанном статусе" как с первоклассным состоянием системы.
Что за смешанный статус? Есть "начали резервацию", "закончили резервацию".
S>·>Это я плохо объяснил. Батчинг — это не требование бизнеса, а механизм повышения перформанса. Это когда запрос в субд на резервацию одной строки и ста строк занимает примерно одно и то же время, т.к. бОльшая часть времени уходит на всякие накладные расходы взаимодействия сервиса резервации и субд. S>Про этот батчинг я ничего не говорил. Я говорил про семантику объединения операций в ACID-группу и техническую реализацию такой семантики.
Не понятно тогда зачем тебе именно надо в данной задаче объединять операции в одну ACID-группу. Из обоснований я пока заметил лишь: "SQL прекрасен батч-операциями".
S>·>Следовательно, выгоднее отправлять стейтменты всегда по 100 штук, вне зависимости от того, разные это заказы или один большой. S>Не совсем. Если вы попробуете сделать 1 коммит на пачку заказов, то в случае неудачи откатится вся транзакция, включая и те заказы, которым всего хватает.
Мы вроде рассматриваем вариант "просто добавляется ещё один предикат во where". Неудача и откат тут будет лишь в случае системной ошибки.
S>·>Т.е. вначале разеделяем заказы на мелкие сообщения резервации, сообщения (возможно батчами) засылаем в очередь. Эта очередь потом разгребается (возможно батчами) и ответы отсылаются таким же способом. Ответы резервации аггрерируются в суммарный статус заказа. S>Увеличиваем латентность, уменьшаем throughput.
Почему уменьшаем?? Для мелких ордеров throughput увеличится засчёт уменьшения числа раундтрипов.
S>·>Что-то вроде многопоточки, асинхронное программирование?.. Акторы?.. S>Ага. Вот мы сейчас изобретаем некоторый способ проектировать подобные системы так, чтобы потенциальные лайвлоки ловить не в продакшне, а в дизайн-тайме. S>Что-то мне подсказывает, что у вас такого способа нет
Ах, вспомнил. Ты же говорил — workflow, который у тебя уже есть всё равно. Вот его и достаточно. Машина состояний, реагирует на сообщения, меняет внутреннее состояние, посылает сообщения.
S>·>Это по сути как реальность работает на низком уровне. REST-запрос — это два коррелированных сообщения запрос-ответ. А если не ограничиваться этим, то можно слать сообщения куда-то, и как-то реагировать на ответы откуда-то. S>Ну так как только мы перестаём "ограничиваться", так сразу теряем всю привычную REST-семантику с её гарантиями.
"привычную" — ключевое слово.
S>·>Ну в трейдинге тоже ничего разъезжаться не должно. Если разъедится — дело дрянь и серьёзные убытки. S>В трейдинге инварианты значительно более простые.
Трейдинг он разный..
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[51]: Помогите правильно спроектировать микросервисное при
Здравствуйте, gandjustas, Вы писали:
G>·>Так ведь по большому счёту разница невелика с т.з. усилий на разработку. А перформанс, он и в африке перформанс. G>Разница огромная. G>В бизнес-приложениях: G>- ключевую роль играет стоимость разработки. Никому не нужно бизнес-приложение, которое стоит дороже самого бизнеса, какое бы крутое это приложение не было. Это линейный фактор. G>- не надо соревноваться по скорости с другими системами. Поэтому перформанс в них — гигиенический фактор. Достигая определенного уровня он перестает давать ценность. Зато стоимость каждого следующего улучшения перформанса растет геометрически. Если разницу между 100мс и 20мс никто глазами не заметит, то нет смысла делать даже 20 мс.
Пока ВНЕЗАПНО не наступает какая-нибдуь чёрная пятница или новогодние распродажи.
G>- высокие требования к консистентности данных. потерять изменения в бизнес-приложении можно один максимум раз, после второго раза будет уже другой исполнитель. Это must-фактор. Нельзя иметь надежность ниже определенного уровня, в сценариях, которые встречаются на практике.
В трейдинге плюс к этому ещё и штрафы влепят от всяких FCA.
G>В трейдинге другие факторы: G>- перформанс это must фактор, так как если ты работаешь медленее конкурентов, то ты просто не достигаешь целей. G>- консистентность вообще можно не учитывать, так как все денные в программе уже устарели G>- стоимость разработки программы на фоне размера счета — погрешность округления
Ээ, так ещё есть трейдинг по другую сторону баррикады. Например, сама биржа по сути те же заказы (orders) и склад (биржевой стакан). И продать больше — никак нельзя.
G>Ну и количество сценариев в бизнес-приложении примерно на порядок или на два меньше, чем в трейдинге.
"меньше"? Ты хотел сказать "больше"?
G> В трейинге по сути один сценарий — приход информации от биржи, а в результате надо выдать пачку ордеров
Ну вот по обсуждаемой теме есть ещё например portfolio/index трейдинг, где ордера бывают на десяток тысяч позиций. И сложные сценарии price negotiation, споттинг, букинг и т.п.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>Здравствуйте, gandjustas, Вы писали:
G>>·>Так ведь по большому счёту разница невелика с т.з. усилий на разработку. А перформанс, он и в африке перформанс. G>>Разница огромная. G>>В бизнес-приложениях: G>>- ключевую роль играет стоимость разработки. Никому не нужно бизнес-приложение, которое стоит дороже самого бизнеса, какое бы крутое это приложение не было. Это линейный фактор. G>>- не надо соревноваться по скорости с другими системами. Поэтому перформанс в них — гигиенический фактор. Достигая определенного уровня он перестает давать ценность. Зато стоимость каждого следующего улучшения перформанса растет геометрически. Если разницу между 100мс и 20мс никто глазами не заметит, то нет смысла делать даже 20 мс. ·>Пока ВНЕЗАПНО не наступает какая-нибдуь чёрная пятница или новогодние распродажи.
ВНЕЗАПНО длительность транзакции напрямую не влияет на масштабируемость систем. По сути все распределенные транзакции длительность увеличивают, но также увеличивают и пропускную способность.
G>>- высокие требования к консистентности данных. потерять изменения в бизнес-приложении можно один максимум раз, после второго раза будет уже другой исполнитель. Это must-фактор. Нельзя иметь надежность ниже определенного уровня, в сценариях, которые встречаются на практике. ·>В трейдинге плюс к этому ещё и штрафы влепят от всяких FCA.
За что?
G>>В трейдинге другие факторы: G>>- перформанс это must фактор, так как если ты работаешь медленее конкурентов, то ты просто не достигаешь целей. G>>- консистентность вообще можно не учитывать, так как все денные в программе уже устарели G>>- стоимость разработки программы на фоне размера счета — погрешность округления ·>Ээ, так ещё есть трейдинг по другую сторону баррикады. Например, сама биржа по сути те же заказы (orders) и склад (биржевой стакан). И продать больше — никак нельзя.
В на бирже чуть проще, там же ордеры по одному инструменту по сути уже сериализованы этим самым "стаканом". остается только инженерная задача распределения "стаканов" по сервакам так, чтобы максимизировать надежность и скорость записи (шардирование)
G>>Ну и количество сценариев в бизнес-приложении примерно на порядок или на два меньше, чем в трейдинге. ·>"меньше"? Ты хотел сказать "больше"?
Да, не исправил при переписывании
G>> В трейинге по сути один сценарий — приход информации от биржи, а в результате надо выдать пачку ордеров ·>Ну вот по обсуждаемой теме есть ещё например portfolio/index трейдинг, где ордера бывают на десяток тысяч позиций. И сложные сценарии price negotiation, споттинг, букинг и т.п.
Что из этого не укладывается в сценарий: получить данные от биржи и выдать ордеры?
Re[53]: Помогите правильно спроектировать микросервисное при
Здравствуйте, gandjustas, Вы писали:
G>·>Пока ВНЕЗАПНО не наступает какая-нибдуь чёрная пятница или новогодние распродажи. G>ВНЕЗАПНО длительность транзакции напрямую не влияет на масштабируемость систем. По сути все распределенные транзакции длительность увеличивают, но также увеличивают и пропускную способность.
А я и не предлагал распределённые транзакции.
G>>>- высокие требования к консистентности данных. потерять изменения в бизнес-приложении можно один максимум раз, после второго раза будет уже другой исполнитель. Это must-фактор. Нельзя иметь надежность ниже определенного уровня, в сценариях, которые встречаются на практике. G>·>В трейдинге плюс к этому ещё и штрафы влепят от всяких FCA. G>За что?
За неимение надёжности определённого уровня при предоставлении финансовых услуг.
G>·>Ээ, так ещё есть трейдинг по другую сторону баррикады. Например, сама биржа по сути те же заказы (orders) и склад (биржевой стакан). И продать больше — никак нельзя. G>В на бирже чуть проще, там же ордеры по одному инструменту по сути уже сериализованы этим самым "стаканом".
Есть ещё mass order cancel/replace которые валят от market makers.
G>остается только инженерная задача распределения "стаканов" по сервакам так, чтобы максимизировать надежность и скорость записи (шардирование)
Круто наверное это твоё шардирование, особенно когда 95% трафика сыплет на какую-нибудь одну особо популярную сковородку типа EURUSD в первые секунды какого-нибудь очередного market event.
G>>> В трейинге по сути один сценарий — приход информации от биржи, а в результате надо выдать пачку ордеров G>·>Ну вот по обсуждаемой теме есть ещё например portfolio/index трейдинг, где ордера бывают на десяток тысяч позиций. И сложные сценарии price negotiation, споттинг, букинг и т.п. G>Что из этого не укладывается в сценарий: получить данные от биржи и выдать ордеры?
Получаем RFQ, прогоняем разные pre-trade проверки, check fail approvals, шлём запрос прайсерам, выбираем лучшую цену, запускаем проверки цены, публикуем цену, обрабатываем client counter, dealer counter, manual intervention, выполняем last-look, выполняем вычисление spot-цены, отсылаем heding, отсылаем booking.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[52]: Помогите правильно спроектировать микросервисное при
Здравствуйте, ·, Вы писали:
·>... часть разработки.
Ну, ок ·>Что за смешанный статус? Есть "начали резервацию", "закончили резервацию".
А что у нас между первым и вторым? Когда половина позиций зарезервирована, а половина — нет.
Оплату мы тоже берём по частям?
·>Не понятно тогда зачем тебе именно надо в данной задаче объединять операции в одну ACID-группу. Из обоснований я пока заметил лишь: "SQL прекрасен батч-операциями".
Прекрасность SQL играет тогда, когда мы приняли решение объединять операции. Во время обработки этой ACID-группы нам противопоказаны любые коммуникации, т.к. они ведут к неограниченным задержкам.
·>Мы вроде рассматриваем вариант "просто добавляется ещё один предикат во where". Неудача и откат тут будет лишь в случае системной ошибки.
Да, это способ развернуть задачу задом наперёд — когда мы, к примеру, резервируем за одно обращение группу позиций по одному и тому же товару для ряда заказов.
S>>Увеличиваем латентность, уменьшаем throughput. ·>Почему уменьшаем?? Для мелких ордеров throughput увеличится засчёт уменьшения числа раундтрипов.
За счёт накладных расходов на "переоркестрацию" заказов.
·>Ах, вспомнил. Ты же говорил — workflow, который у тебя уже есть всё равно. Вот его и достаточно. Машина состояний, реагирует на сообщения, меняет внутреннее состояние, посылает сообщения.
Набор гарантий для такой машины очень небольшой. Доказать, что в такой системе любой заказ рано или поздно станет либо "отказанным", либо "зарезервированным" — то ещё упражнение.
Даже если отказаться от гарантий liveness и сосредоточиться на safety — что мы никогда не получим дедлок — это представляет собой крайне сложную задачу, для которой нет общего решения.
S>>Ну так как только мы перестаём "ограничиваться", так сразу теряем всю привычную REST-семантику с её гарантиями. ·>"привычную" — ключевое слово.
Именно.
·>Трейдинг он разный..
Ну, когда в руках молоток — всё кажется гвоздями. И это, к сожалению, работает для любого подхода.
Когда в руках reader_writer_lock, то кажется, что невозможно обойтись без захвата/отпускания N*M блокировок, где N — количество позиций заказов в системе, а M — количество стадий конвеера обработки заказа.
Когда в руках — LMAX Disruptor, то кажется, что любую деятельность можно представить как цепочку очередей событий.
И гарантии, которые в одном подходе легко и элегантно обеспечиваются "по построению", в другом подходе представляют собой нетривиально доказываемую (если вообще доказуемую) теорему.
И, соответственно, наоборот.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[53]: Помогите правильно спроектировать микросервисное при
Здравствуйте, Sinclair, Вы писали:
S>·>Что за смешанный статус? Есть "начали резервацию", "закончили резервацию". S>А что у нас между первым и вторым? Когда половина позиций зарезервирована, а половина — нет. S>Оплату мы тоже берём по частям?
Продолжаем резервацию до победного конца. Ты ж сам тут всё описывал: https://rsdn.org/forum/design/9057018
. Мой поинт был лишь в том, что нет никакой нужды при таком подходе пытаться всё заталкивать в одну транзакцию произвольного размера.
S>·>Не понятно тогда зачем тебе именно надо в данной задаче объединять операции в одну ACID-группу. Из обоснований я пока заметил лишь: "SQL прекрасен батч-операциями". S>Прекрасность SQL играет тогда, когда мы приняли решение объединять операции. Во время обработки этой ACID-группы нам противопоказаны любые коммуникации, т.к. они ведут к неограниченным задержкам.
А зачем мы приняли такое решение?
S>·>Мы вроде рассматриваем вариант "просто добавляется ещё один предикат во where". Неудача и откат тут будет лишь в случае системной ошибки. S>Да, это способ развернуть задачу задом наперёд — когда мы, к примеру, резервируем за одно обращение группу позиций по одному и тому же товару для ряда заказов.
Именно. Когда мы не принимаем такие решения на пустом месте, у нас появляется свобода выбора и простор для различных оптимизаций.
S>>>Увеличиваем латентность, уменьшаем throughput. S>·>Почему уменьшаем?? Для мелких ордеров throughput увеличится засчёт уменьшения числа раундтрипов. S>За счёт накладных расходов на "переоркестрацию" заказов.
Эти накладные расходы не требуют сетевых операций или блокировок. А значит практически нулевые, наносекундного диапазона.
S>·>Ах, вспомнил. Ты же говорил — workflow, который у тебя уже есть всё равно. Вот его и достаточно. Машина состояний, реагирует на сообщения, меняет внутреннее состояние, посылает сообщения. S>Набор гарантий для такой машины очень небольшой. Доказать, что в такой системе любой заказ рано или поздно станет либо "отказанным", либо "зарезервированным" — то ещё упражнение. S>Даже если отказаться от гарантий liveness и сосредоточиться на safety — что мы никогда не получим дедлок — это представляет собой крайне сложную задачу, для которой нет общего решения.
Почему? Чем это будет отличаться от того, что ты там выше по пунктам расписывал?
S>·>"привычную" — ключевое слово. S>Именно.
S>·>Трейдинг он разный.. S>Ну, когда в руках молоток — всё кажется гвоздями. И это, к сожалению, работает для любого подхода. S>Когда в руках reader_writer_lock, то кажется, что невозможно обойтись без захвата/отпускания N*M блокировок, где N — количество позиций заказов в системе, а M — количество стадий конвеера обработки заказа. S>Когда в руках — LMAX Disruptor, то кажется, что любую деятельность можно представить как цепочку очередей событий. S>И гарантии, которые в одном подходе легко и элегантно обеспечиваются "по построению", в другом подходе представляют собой нетривиально доказываемую (если вообще доказуемую) теорему. S>И, соответственно, наоборот.
Как раз не молоток. "Это другое". Если вспомнить историю, то этот подход популяризировался на базе Mechanical Sympathy. То есть не построили красивую абстрактность из мира идей того что "нам привычно" и пошли размахивать как молотком, мол, все наши пользователи пойдут в один сервер, или пусть идут нафик. Вот и HTTP, REST у нас, а потом совершенно ВНЕЗАПНО появились Keep Alive, pipelining, WebSocket, QUIC.
А Mechanical Sympathy — это как раз шаг к тому, как реально работают реальные системы в физическом мире. Потому это и есть дорога к сабж.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>Продолжаем резервацию до победного конца.
Это как раз и означает, что у заказа появляется статус "частично зарезервирован", который нужно учитывать во всех смежных системах.
·>А зачем мы приняли такое решение?
Затем, чтобы обеспечить инварианты, принятые в бизнес-домене. ·>Именно. Когда мы не принимаем такие решения на пустом месте, у нас появляется свобода выбора и простор для различных оптимизаций.
Простор для оптимизаций появляется обычно там, где есть хорошо обоснованная эквивалентность. Вот, в частности, именно за это полюбили реляционную алгебру — она даёт дешёвую возможность строго доказать эквивалентность различных планов запросов, что и открывает простор оптимизациям, недостижимым для альтернативных моделей.
·>Эти накладные расходы не требуют сетевых операций или блокировок. А значит практически нулевые, наносекундного диапазона.
Вообще-то требуют, т.к. нам нужно обеспечить какие-то простейшие вещи — например, что кто-то не пытается изменить состав заказа одновременно с тем, как отрабатывает наш код резервирования. И если для одиночного заказа это "точечная" блокировка, которая не влияет на обработку остальных заказов, то "перегруппировка позиций" уже затрагивает пачку заказов.
·>Почему? Чем это будет отличаться от того, что ты там выше по пунктам расписывал?
Потому что один процесс у вас копает ямы, другой — закапывает. А тот, который должен был сажать деревья, сообщение не получил. Каждый процесс пашет в соответствии со своим ТЗ, и все юнит-тесты проходят, а результата нет.
·>Как раз не молоток. "Это другое". Если вспомнить историю, то этот подход популяризировался на базе Mechanical Sympathy. То есть не построили красивую абстрактность из мира идей того что "нам привычно" и пошли размахивать как молотком, мол, все наши пользователи пойдут в один сервер, или пусть идут нафик. Вот и HTTP, REST у нас, а потом совершенно ВНЕЗАПНО появились Keep Alive, pipelining, WebSocket, QUIC. ·>А Mechanical Sympathy — это как раз шаг к тому, как реально работают реальные системы в физическом мире. Потому это и есть дорога к сабж.
Всякий раз, как я вижу упоминание "как реально работают реальные системы в реальном мире", я чую булшит. Это и про ООП говорили, и даже немножко про фп.
Нам не особо нужно моделировать физический мир. Нам нужно моделировать решение задачи И какой формализм выбрать — state propagation или communicating automata — зависит от того, какие конкретно цели мы преследуем.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[55]: Помогите правильно спроектировать микросервисное при
Здравствуйте, Sinclair, Вы писали:
S>·>Продолжаем резервацию до победного конца. S>Это как раз и означает, что у заказа появляется статус "частично зарезервирован", который нужно учитывать во всех смежных системах.
Не понял где и откуда и зачем.
S>·>А зачем мы приняли такое решение? S>Затем, чтобы обеспечить инварианты, принятые в бизнес-домене.
Давай конкретно. Вот у тебя там был процесс из 11 пунктов. Что конкретно не будет обеспечиваться, если мы резервацию будем делать построчно?
Вот там у тебя было: "Конкретный SQL тут не так важен — важна его атомарность. Если мы всё же довели транзакцию до конца и получили в сервисе А положительный ответ от СУБД". С этим я не спорю. Я лишь уточнил, что тебе важна атомарность изменения одной строки stock и соответствующей ей строки reservationItems. Но нам не важна атомарность между всеми позициями заказа. Мы просто загоняем каждую позицию в отдельную независимую транзакцию в тупом for-цикле.
S>·>Именно. Когда мы не принимаем такие решения на пустом месте, у нас появляется свобода выбора и простор для различных оптимизаций. S>Простор для оптимизаций появляется обычно там, где есть хорошо обоснованная эквивалентность. Вот, в частности, именно за это полюбили реляционную алгебру — она даёт дешёвую возможность строго доказать эквивалентность различных планов запросов, что и открывает простор оптимизациям, недостижимым для альтернативных моделей.
Неясно эквивалентность чего к чему подразумеваешь в контексте этого обсуждения.
S>·>Эти накладные расходы не требуют сетевых операций или блокировок. А значит практически нулевые, наносекундного диапазона. S>Вообще-то требуют, т.к. нам нужно обеспечить какие-то простейшие вещи — например, что кто-то не пытается изменить состав заказа одновременно с тем, как отрабатывает наш код резервирования.
В твоём решении с одной транзакцией это тоже надо делать. Впрочем, это делается просто — изменять состав заказа можно только в определённых статусах заказа. Если статус "резервируется" — менять нельзя, ни в моём случае, ни в твоём. Та же песня.
S>И если для одиночного заказа это "точечная" блокировка, которая не влияет на обработку остальных заказов, то "перегруппировка позиций" уже затрагивает пачку заказов.
Не вижу как именно затрагивает.
S>·>Почему? Чем это будет отличаться от того, что ты там выше по пунктам расписывал? S>Потому что один процесс у вас копает ямы, другой — закапывает. А тот, который должен был сажать деревья, сообщение не получил. Каждый процесс пашет в соответствии со своим ТЗ, и все юнит-тесты проходят, а результата нет.
По ТЗ процесс закапывающий ямы получает сообщения от процесса-сажателя. С какого бодуна закапыватель чего-то начнёт делать до отсылки сообщения ему?
S>·>Как раз не молоток. "Это другое". Если вспомнить историю, то этот подход популяризировался на базе Mechanical Sympathy. То есть не построили красивую абстрактность из мира идей того что "нам привычно" и пошли размахивать как молотком, мол, все наши пользователи пойдут в один сервер, или пусть идут нафик. Вот и HTTP, REST у нас, а потом совершенно ВНЕЗАПНО появились Keep Alive, pipelining, WebSocket, QUIC. S>·>А Mechanical Sympathy — это как раз шаг к тому, как реально работают реальные системы в физическом мире. Потому это и есть дорога к сабж. S>Всякий раз, как я вижу упоминание "как реально работают реальные системы в реальном мире", я чую булшит. Это и про ООП говорили, и даже немножко про фп.
Говорили то, что говоришь и ты: "можно представить". Тут ведь как... можно представить, а ведь можно и не представить!
Взаимодействие в физическом мире происходит посредством посылки сигналов-сообщений. Открой учебник по ТО — там будут signals-events. Произошло событие — полетел фотон. Долетел куда-то — произошло новое событие, фотоны поглотились, излучились новые.
S>Нам не особо нужно моделировать физический мир. Нам нужно моделировать решение задачи И какой формализм выбрать — state propagation или communicating automata — зависит от того, какие конкретно цели мы преследуем.
Вот только реальности всё равно какой формализм ты выбрал — по проводу пойдёт именно коммуникационный сигнал, а не состояние. Тебе придётся построить кривенькую модель организации передачи состояния посредством сигналов. И удивиться потом, чегой-то оно тормозит и состояния разъезжаются...
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай