Да, возможно. С этим не сталкивался совсем. Подозреваю, там какие-то отдельные исторические таблицы ведутся для тех счетов, где это надо.
В основной таблице счетов этого нет - там и так очень много всяких данных (дата открытия/закрытия счета, бухрежим по счету еще куча всяких признаков) - дублировать все это в историю нет смысла.
Не. Я ж не по бухгалтерии. Я по банку. Тут немного иначе все.
А работаю на уровне АБС. Т.е. то, что на центральных серверах. Но направление - автоматизация процессов комплаенс-контроля + клиентские данные. Со счетами мало работаем, наше - это во-первых, типы клиентов (сами клиенты, доверенные и уполномоченные лица, держатели карт и все вот это вот), списки росфина (экстремисты-террористы) и совпадения субъектов списков с клиентами, всякие ДУЛы (документы удостоверяющие личность), адреса и т.п.
Ну и проверки разные, само собой. Т.е. приходит некий СУЛТАНОВ ЛЕЧИ РАСУЛОВИЧ и говорит "хочу счет открыть у вас". Девочка начинает его данные вводить в систему, а ей выскакивает "найдено совпадение со списком ПЭ (подозреваемых в экстремизме)". Наша проверка отработала на попытку ввода данных.
Аналогично если кто-то из клиентов попробует этому персонажу деньги куда-то в другой банк перевести. Тоже сработает - платеж отправится на ручной контроль с службу комплаенса, те уже будут детально изучать что к чему (и скорее всего заблокируют перевод).
Закрытый опердень это из бухгалтерской лексики, про отражение операций в бухучёте. У них, как правило, несколько опердней открыто одновременно, так как документы для отражения в учёте могут досылаться несколько дней.
В банке с этим проще, слава богу. Тут все в текущем дне делается т.к. работа с платежными поручениями идет.
Строго говоря, я не встречал где было бы нужна история балансов по счетам на конкретные даты. История операций - да (выписка по счету). Текущий баланс и холды - да. Но вот вся история балансов и холдов... Впрочем, я со счетами работаю мало - мое направление это комплаенс и клиентские данные.
Со счетами приходится работать, но косвенно. Типа такого:
1. Отбираем клиентов ФЛ открывшие счет на определенных балансовых позициях в предыдущую дату:
1.1. отбираем базовый номер клиента:
1.1.1. счет в статусе «Открыт» и счет не зарезервирован;
1.1.2. счет открыт день назад;
1.1.3. маска счета входит в список разрешенных;
1.1.4. исключить запрещенные маски счетов
Или
Рассчитать сумму займа, получив актуальный курс, и сконвертированную в рублёвый эквивалент сумму на счетах, тип которых входит в список ...
Рассчитать сумму вклада, получив актуальный курс, и сконвертированную в рублёвый эквивалент сумму на счетах, тип которых входит в список ...
Или
1. Для отправителя и получателя выполнить проверки:
1.1. Получить все незарезервированные счета клиента, маски которых начинаются на ...
1.2. Определить самую раннюю дату открытия счета
Если дата меньше или равна граничной ...
Ну и так далее... Это уже из комплекса "Онлайн контроль платежей" (собственно, это команда Системы Расчетов делает, мы только делали комплекс комплаенс-проверок для них).
про "закрытый опердень" не слышал. Возможно, мы работает несколько по иной схеме.
Обычная работа - "дневной юнит". Это текущий день. Примерно в полночь по мск начинается процедура закрытия опердня и перехода на новый (у нас это называется EOD - End-Of-Day). Длится она 3-4 часа примерно - это фактически сведение всего баланса.
Чтобы операции могли продолжаться в течении EOD, перед его началом создается т.н. "юнит ночного ввода" - копия дневного юнита на момент начала EOD. Дальше, в дневном юните начинается процедура EOD, а все операции продолжатся в ночном юните.
Когда EOD завершился и дневной юнит перешел на новый день, в него "накатывается" (по журналам, писал тут про это уже) все, что случилось в ночи. Т.е. все изменения, которые произошли в течении EOD. И дальше уже обычная работа в новом дне.
В целом, это не единственная схема. Есть и другие. Например, без ночного юнита (т.е. в момент EOD никакие операции не проводятся). Там работает "кешир" - все операции ставятся в очередь (кеш), которая потом будет разбираться после завеhшения EOD уже в новом дне.
Считается, что наша схема работает "в реальном времени" т.е. банк работает без перерыва на EOD, схема с кеширом - в перерывом (операции принимаются к исполнению, но откладываются до следующего дня и реально будут совершены уже в следующем дне).
Возможно, есть и другие схемы работы в реальном времени, но там подробностей не знаю.
Честно говоря, АБС в банке настолько сложная и большая система, с таким большим количеством бизнес-процессов, что так просто описать ее практически невозможно. Только в каких-то общих словах.
В банке по счету хранится текущий баланс счета, сумма текущих холдов (незавершенные операции) + "платежные документы" - от кого, кому, сумма. С привязкой к счету.
Естественно, показать операции за сутки, неделю, месяц нет проблем. Это называется "выписка по счету". Просто нужно сделать выборку всех платежных документов, привязанных к этому счету.
Ну на самом деле Холды часто бывают не нулевые. Например, на счет у вас 10 000р На холде 0. Пошли по магазинам - тут купили на 1 000, там 2 000, здесь на 3 000... В результате "на счете" 4 000, на холде 6 000. Потом, когда начинают приходить подтверждения по платежным операциям, холд начинает уменьшаться (каждый раз на ту сумму, на которую пришло подтверждение).
Или на заправке - оплачиваете бензина на 2 500р Они встают на холд. Но в бак влезло только на 2 400р. Тут возможны варианты (это уже как банк отработает). Или сначала отмена все операции на 2 500р с возвратом всей суммы с холда на счет и сразу создание новой операции уже на 2 400р с перемещением на холд со счета 2 400р. Или 2 400р остаются на холде до подтверждения , а 100р оформляются как "возврат" т с холда переходят обратно на счет. И так и этак встречал.
Скорее всего проблема в микросервисах у которых каждый имеет свою копию БД. И начинаются задержки репликации и синхронизации этих копий. Тут от микросервисов бльше проблем чем пользы.
На самом деле там все не так. А примерно так:
С баланса счета плательщика сумма переводится на холд
Проводится комплекс проверок платежа.
Если все проверки пройдены, сумма зачисляется на счет получателя
При успешном зачислении суммы на счет получателя она списывается с холда на счету плательщика.
Это универсальная схема как для случая когда счета в одном, так и для случая когда счета в разных банках.
При запросе баланса счета показывается только баланс, без учета холдов. Работаем с одной БД, без репликаций (точнее, они есть, но для разных "внешних систем" и там, естесвтенно, не вся БД, а только нужные этим внешним системам данные реплицируются). И да, там могут быть лаги, но для тех систем это несущественно по их логике работы.
И дальше, похоже, вручную делается то, что в более простых случаях (или более умных базах) делается самим движком базы данных.
Тут вопрос не в том, что может БД. Она (DB2 for i) много что может.
Вопрос в производительности в условиях больших нагрузок. Наши исследования показали что SQL может начать работать нестабильно (в смысле скорости выполнения) в ситуациях, когда один и тот же запрос начинает выполняться параллельно из многих заданий (job) с большой плотностью вызовов. При этом прямая работа с БД не только работает стабильно в таких условиях, но и еще обеспечивает меньшую загрузку системы.
Поэтому на использование SQL есть ряд ограничений, сформулированных в нефункциональных требованиях, основанных на реальной практике работы системы.
Это то, что бывает сложно понять людям, не имеющим иных возможностей работы с БД, помимо SQL. Но у нас, слава богу, такие возможности в системе заложены.
Ну и SQL (точнее, commitment control) не решает проблему журналирования, возможность которого для нашей архитектуры является ключевой.
Без уточнения что происходит - не гонка? Потому что между "еще раз читаем с проверкой' и 'то записываем изменения' - записи могут и измениться.
Да. Могут измениться. Именно поэтому и проводится проверка с выдачей ошибки. Как эта ошибка обрабатывается - тут все зависит от бизнес-логики процеса.
Но блокировка записи на длительное время (пока идет какая-то обработка - это прямой путь к дедлокам и деградации производительности. Что в нашем случае неприемлемо.
Я описал как в реальности происходит обработка платежа - кратковременная блокировка при переносе суммы платежа с баланса на холд. Дальше платеж может обрабатываться сколь угодно долго - баланс счета уже уменьшился, но в случае отката (отказа в проведении платежа) может быть увеличен обратно обратной операцией - возврата суммы с холда обратно на баланс. И транзакцией это не решается т.к. вы не можете в такой системе держать транзакцию открытой в течении длительного времени (а это может достигать суток - пока пройдет проверки, пройдет при необходимости ручной контроль, будет получено подтверждение из того банка, на счет в котором уходит платеж...)
А чтобы перевести со счета на холд транзакция не нужна - это одна запись (счет - сумма текущего баланса - сумма холдов по счету) - read (с блокировкой), cerbal -= n, curhold += n, update (с разблокировкой). А транзакция по определению, это цепочка связанных изменений в нескольких записях.
К сожалению, там нет ответа на поставленный вопрос.
Я уже писал как это происходит в реальной жизни.
Формируется платежный документ где указывается плательщик, получатель, счет плательщика, счет получателя. Причем, эти счета на обязательно в одном банке. Скорее всего, даже в разных. И сложности возникают только со списанием денег со счета плательщика. С зачислением на счет получателя все проще.
Если счет плательщика в нашем банке, Сумма по платежному документу переводится со счета на холд - резервируется (если на счете достаточно денег). Это быстрая операция, она делается с блокировкой записи.
Платежный документ отправляется на контроль. Результатом может быть безусловное "разрешить" (все автоматические проверки пройдены) - тогда документ передается на исполнение. Или документ может быть отправлен на ручной контроль в службу комплаенса. Оттуда может прийти решение "разрешить" - тогда на исполнение. Или "запретить" - тогда формируется отказ в операции и сумма с холда возвращается обратно на счет (опять с полной блокировкой записи - это одна запись, там есть поле текущего баланса, есть поле суммарного холда). В целом контроль платежей штука достаточно сложная - там много разных проверок проводится.
Сумма холда уменьшается на сумму платежного документа только тогда, когда от получателя придет подтверждение о том, что деньги реально пришли на его счет (иногда это может занимать несколько секунд, иногда несколько минут).
Т.е. одной транзакцией тут никак не обойтись - между переводом денег с баланса на холд и списанием их с холда или возвратом обратно на баланс (в случае отказа в операции или неподтверждения операции в установленные сроки) может пройти существенное время.
Это и есть та самая (причем, достаточно упрощенная) бизнес-логика, в отрыве от которой все это превращается в сферического коня в вакууме.
Кроме того, транзакции достаточно сильно грузят сервер. Я скажу крамольную вещь, но мы работаем с COMMITMENT CONTROL (*NONE). Т.е. без коммитов и роллбеков. Вместо этого ведется журналирование всех операций (в специальные журналы пишутся образы записей "до" и "после" изменения (или только "после" в случае добавления или только "до" в случае удаления). Плюс двойное чтение - прочитали запись "до", внесли изменения (получили "после"), перед записью изменений еще раз читаем запись с проверкой - совпадет она с образом "до" - если да, то записываем изменения, если нет - возвращаем ошибку "кто-то другой изменил запись" (и дальше она уже по бизнес-логике обрабатывается).
По журналам можно откатить что угодно - хоть вчерашнее, хоть позавчерашнее. Кроме того, по журналам в начале нового дня накатываются все изменения из юнита ночного ввода (писал ранее об этом).
Правда, опять крамола, мы не используем SQL для изменений в продуктовых таблицах. Наш стек позволяет напрямую работать с БД (SQL используется там, где нужный сложные выборки по нескольким таблицам и многим условиям, и то, там не так все просто с точки зрения производительности).
Строго говоря, для каждой продуктовой таблицы есть "опция ведения". Которая состоит из 4-х модулей:
Update модуль - собственно работа с таблицей и ее журналом. Чтение образа "до", запись образа "после" (с проверкой что "до" не изменился). В реальной жизни может быть достаточно сложным т.к. может работать не с одной, а с несколькими логически связанными таблицами.
Validate модуль - контроль валидности данных записи в соответствии с бизнес-логикой
Модуль интерактивной работы с записью (позволяет вносить ручные изменения). Вводим уникальный ключ - если запись есть - работаем режиме изменения, если нет - в режиме добавления. После внесенных изменений вызывается Validate модуль и, если ошибки нет - Update модуль.
Модуль внешнего ввода. Получает на вход образ "после", читает из таблицы образ "до" (опять, есть запись - изменение, нет - добавление), вызывает Validete модуль, если ошибок нет - Update модуль. Этот же модуль позволяет делать "накат по журналу". Для этого на вход передается не образ "после", а имя библиотеки где лежит журнал и ключ записи с образом "после". В этом случае образ "после" берется из журнала.
Данная схема кажется сложной, но стабильно работает в условиях очень больших нагрузок.
И все равно вопрос - вы начинате транзакцию на списание 300р со счета. И вэтот же момент начинается транзакция на списание 400р. А на счете вмего 500р. Каждая транзакция по отдельности валидна. Вместе - нет. Что делать?
Вообще, это основа. Прочитал запись, что-то в ней сделал, перед тем как записывать - проверь. Прочитай еще раз - не изменилось ли что-то пока ты работал с записью
И еще раз - что делать когда счета плательщика и аолучателя в разных банках?
Что делать, когда платкж по каким-то причинам отправлен на ручной контроль и подтверждение его будет через час, два а то и вообще только завтра?
Еще более сложное. В банке каждый день наступает фаза закрытия опредня и перехода на следующий день. Фактически - сведение баланса. Но при этом платежи принимаются и обрабатываются. Но не в том юните, где идет переход на следующий лень, а в другом - юните ночного ваода. А потом, когда основной юниттперешел на следующий день, в него накатываются все изменения из ночи... Как с этим быть?
Статья про сферического коня в вакууме, а не про реальную жизнь.
Вот поэтому и создается платежный документ, сумма ставится на холд, а потом уже платежный документ проверяется. И если он подтвержден, тога уже осуществляется сам перевод. Деньги при этом списываются с холда.
Если платкж заблокировпн, сумма с холда возвращается обратно.
Описывать технические проблемы в отрыве от бизеслогики в корне неверно
Очень похоже на попытку реализовать обычный динамический массив, но как-то не до конца...
Неочевидна стратегия расширения массива (сколько памяти выделяется когда емкость исчерпана и пытаемся добавить новый элемент?). Не видно итераторов. Не видно интерфейсов обращения к произвольному элементу массива...
Если это делалось специально под конкретную задачу, то хорошо бы описать что именно за задача, какие требования и сценарии использования всего этого.
Естественно. Если вместо простого if поставить вызов какой-то функции (с разворачиванием-сворачиванием стека, а еще, не дай бог, выбросом исключения), а потом вызвать все это 100500 раз, разница в производительности будет заметна. Но кого все это волнует? Главное же концептуальность. А если у потребителя все это начинает тормозить - пусть заменит один старый сервер на два новых, помощнее и подороже.
Зато когда MS заявляет что поддержка Win10 заканчивается, а для перехода на Win11 потребуется обновить комп, нас это дико возмущает - да как они посмели...
Сложные конструкции вообще сложно понимать :-) Абстракции плохи тем, что когда нужно понять логику работы программы (а в жизни неоднократно приходилось сталкиваться с весьма и весьма мудреной и неочевидной бизнеслогикой), приходится работать одновременно с несколькими исходниками, отслеживая каждый вызов "до самого дна".
А уж когда нужна аналитика типа "кто использует этот модуль" (на предмет оценки рисков и объема регресс-тестов при его изменении), а там начинается - этот модуль используется модулем А, который вызывается из модуля Б, а тот задействован в модуле В...
Но не суть.
Простота чтения кода - это не про использование или не использование if. Это про стиль. Многовложенные if, конечно ничуть не проще в понимании чем пирамида многоуровневых абстракций... Так что тут стоит использовать все средства, которые могут облегчить читаемость кода.
Да, возможно. С этим не сталкивался совсем. Подозреваю, там какие-то отдельные исторические таблицы ведутся для тех счетов, где это надо.
В основной таблице счетов этого нет - там и так очень много всяких данных (дата открытия/закрытия счета, бухрежим по счету еще куча всяких признаков) - дублировать все это в историю нет смысла.
Не. Я ж не по бухгалтерии. Я по банку. Тут немного иначе все.
А работаю на уровне АБС. Т.е. то, что на центральных серверах. Но направление - автоматизация процессов комплаенс-контроля + клиентские данные. Со счетами мало работаем, наше - это во-первых, типы клиентов (сами клиенты, доверенные и уполномоченные лица, держатели карт и все вот это вот), списки росфина (экстремисты-террористы) и совпадения субъектов списков с клиентами, всякие ДУЛы (документы удостоверяющие личность), адреса и т.п.
Ну и проверки разные, само собой. Т.е. приходит некий СУЛТАНОВ ЛЕЧИ РАСУЛОВИЧ и говорит "хочу счет открыть у вас". Девочка начинает его данные вводить в систему, а ей выскакивает "найдено совпадение со списком ПЭ (подозреваемых в экстремизме)". Наша проверка отработала на попытку ввода данных.
Аналогично если кто-то из клиентов попробует этому персонажу деньги куда-то в другой банк перевести. Тоже сработает - платеж отправится на ручной контроль с службу комплаенса, те уже будут детально изучать что к чему (и скорее всего заблокируют перевод).
В банке с этим проще, слава богу. Тут все в текущем дне делается т.к. работа с платежными поручениями идет.
Строго говоря, я не встречал где было бы нужна история балансов по счетам на конкретные даты. История операций - да (выписка по счету). Текущий баланс и холды - да. Но вот вся история балансов и холдов... Впрочем, я со счетами работаю мало - мое направление это комплаенс и клиентские данные.
Со счетами приходится работать, но косвенно. Типа такого:
Или
Или
Ну и так далее... Это уже из комплекса "Онлайн контроль платежей" (собственно, это команда Системы Расчетов делает, мы только делали комплекс комплаенс-проверок для них).
про "закрытый опердень" не слышал. Возможно, мы работает несколько по иной схеме.
Обычная работа - "дневной юнит". Это текущий день. Примерно в полночь по мск начинается процедура закрытия опердня и перехода на новый (у нас это называется EOD - End-Of-Day). Длится она 3-4 часа примерно - это фактически сведение всего баланса.
Чтобы операции могли продолжаться в течении EOD, перед его началом создается т.н. "юнит ночного ввода" - копия дневного юнита на момент начала EOD. Дальше, в дневном юните начинается процедура EOD, а все операции продолжатся в ночном юните.
Когда EOD завершился и дневной юнит перешел на новый день, в него "накатывается" (по журналам, писал тут про это уже) все, что случилось в ночи. Т.е. все изменения, которые произошли в течении EOD. И дальше уже обычная работа в новом дне.
В целом, это не единственная схема. Есть и другие. Например, без ночного юнита (т.е. в момент EOD никакие операции не проводятся). Там работает "кешир" - все операции ставятся в очередь (кеш), которая потом будет разбираться после завеhшения EOD уже в новом дне.
Считается, что наша схема работает "в реальном времени" т.е. банк работает без перерыва на EOD, схема с кеширом - в перерывом (операции принимаются к исполнению, но откладываются до следующего дня и реально будут совершены уже в следующем дне).
Возможно, есть и другие схемы работы в реальном времени, но там подробностей не знаю.
Честно говоря, АБС в банке настолько сложная и большая система, с таким большим количеством бизнес-процессов, что так просто описать ее практически невозможно. Только в каких-то общих словах.
В банке по счету хранится текущий баланс счета, сумма текущих холдов (незавершенные операции) + "платежные документы" - от кого, кому, сумма. С привязкой к счету.
Естественно, показать операции за сутки, неделю, месяц нет проблем. Это называется "выписка по счету". Просто нужно сделать выборку всех платежных документов, привязанных к этому счету.
Ну на самом деле Холды часто бывают не нулевые. Например, на счет у вас 10 000р На холде 0. Пошли по магазинам - тут купили на 1 000, там 2 000, здесь на 3 000... В результате "на счете" 4 000, на холде 6 000. Потом, когда начинают приходить подтверждения по платежным операциям, холд начинает уменьшаться (каждый раз на ту сумму, на которую пришло подтверждение).
Или на заправке - оплачиваете бензина на 2 500р Они встают на холд. Но в бак влезло только на 2 400р. Тут возможны варианты (это уже как банк отработает). Или сначала отмена все операции на 2 500р с возвратом всей суммы с холда на счет и сразу создание новой операции уже на 2 400р с перемещением на холд со счета 2 400р. Или 2 400р остаются на холде до подтверждения , а 100р оформляются как "возврат" т с холда переходят обратно на счет. И так и этак встречал.
Ну не все :-) У нас, слава богу, это дичи нет.
Скорее всего проблема в микросервисах у которых каждый имеет свою копию БД. И начинаются задержки репликации и синхронизации этих копий. Тут от микросервисов бльше проблем чем пользы.
На самом деле там все не так. А примерно так:
С баланса счета плательщика сумма переводится на холд
Проводится комплекс проверок платежа.
Если все проверки пройдены, сумма зачисляется на счет получателя
При успешном зачислении суммы на счет получателя она списывается с холда на счету плательщика.
Это универсальная схема как для случая когда счета в одном, так и для случая когда счета в разных банках.
При запросе баланса счета показывается только баланс, без учета холдов. Работаем с одной БД, без репликаций (точнее, они есть, но для разных "внешних систем" и там, естесвтенно, не вся БД, а только нужные этим внешним системам данные реплицируются). И да, там могут быть лаги, но для тех систем это несущественно по их логике работы.
Тут вопрос не в том, что может БД. Она (DB2 for i) много что может.
Вопрос в производительности в условиях больших нагрузок. Наши исследования показали что SQL может начать работать нестабильно (в смысле скорости выполнения) в ситуациях, когда один и тот же запрос начинает выполняться параллельно из многих заданий (job) с большой плотностью вызовов. При этом прямая работа с БД не только работает стабильно в таких условиях, но и еще обеспечивает меньшую загрузку системы.
Поэтому на использование SQL есть ряд ограничений, сформулированных в нефункциональных требованиях, основанных на реальной практике работы системы.
Это то, что бывает сложно понять людям, не имеющим иных возможностей работы с БД, помимо SQL. Но у нас, слава богу, такие возможности в системе заложены.
Ну и SQL (точнее, commitment control) не решает проблему журналирования, возможность которого для нашей архитектуры является ключевой.
Да. Могут измениться. Именно поэтому и проводится проверка с выдачей ошибки. Как эта ошибка обрабатывается - тут все зависит от бизнес-логики процеса.
Но блокировка записи на длительное время (пока идет какая-то обработка - это прямой путь к дедлокам и деградации производительности. Что в нашем случае неприемлемо.
Я описал как в реальности происходит обработка платежа - кратковременная блокировка при переносе суммы платежа с баланса на холд. Дальше платеж может обрабатываться сколь угодно долго - баланс счета уже уменьшился, но в случае отката (отказа в проведении платежа) может быть увеличен обратно обратной операцией - возврата суммы с холда обратно на баланс. И транзакцией это не решается т.к. вы не можете в такой системе держать транзакцию открытой в течении длительного времени (а это может достигать суток - пока пройдет проверки, пройдет при необходимости ручной контроль, будет получено подтверждение из того банка, на счет в котором уходит платеж...)
А чтобы перевести со счета на холд транзакция не нужна - это одна запись (счет - сумма текущего баланса - сумма холдов по счету) - read (с блокировкой), cerbal -= n, curhold += n, update (с разблокировкой). А транзакция по определению, это цепочка связанных изменений в нескольких записях.
К сожалению, там нет ответа на поставленный вопрос.
Я уже писал как это происходит в реальной жизни.
Формируется платежный документ где указывается плательщик, получатель, счет плательщика, счет получателя. Причем, эти счета на обязательно в одном банке. Скорее всего, даже в разных. И сложности возникают только со списанием денег со счета плательщика. С зачислением на счет получателя все проще.
Если счет плательщика в нашем банке, Сумма по платежному документу переводится со счета на холд - резервируется (если на счете достаточно денег). Это быстрая операция, она делается с блокировкой записи.
Платежный документ отправляется на контроль. Результатом может быть безусловное "разрешить" (все автоматические проверки пройдены) - тогда документ передается на исполнение. Или документ может быть отправлен на ручной контроль в службу комплаенса. Оттуда может прийти решение "разрешить" - тогда на исполнение. Или "запретить" - тогда формируется отказ в операции и сумма с холда возвращается обратно на счет (опять с полной блокировкой записи - это одна запись, там есть поле текущего баланса, есть поле суммарного холда).
В целом контроль платежей штука достаточно сложная - там много разных проверок проводится.
Сумма холда уменьшается на сумму платежного документа только тогда, когда от получателя придет подтверждение о том, что деньги реально пришли на его счет (иногда это может занимать несколько секунд, иногда несколько минут).
Т.е. одной транзакцией тут никак не обойтись - между переводом денег с баланса на холд и списанием их с холда или возвратом обратно на баланс (в случае отказа в операции или неподтверждения операции в установленные сроки) может пройти существенное время.
Это и есть та самая (причем, достаточно упрощенная) бизнес-логика, в отрыве от которой все это превращается в сферического коня в вакууме.
Кроме того, транзакции достаточно сильно грузят сервер. Я скажу крамольную вещь, но мы работаем с COMMITMENT CONTROL (*NONE). Т.е. без коммитов и роллбеков. Вместо этого ведется журналирование всех операций (в специальные журналы пишутся образы записей "до" и "после" изменения (или только "после" в случае добавления или только "до" в случае удаления). Плюс двойное чтение - прочитали запись "до", внесли изменения (получили "после"), перед записью изменений еще раз читаем запись с проверкой - совпадет она с образом "до" - если да, то записываем изменения, если нет - возвращаем ошибку "кто-то другой изменил запись" (и дальше она уже по бизнес-логике обрабатывается).
По журналам можно откатить что угодно - хоть вчерашнее, хоть позавчерашнее. Кроме того, по журналам в начале нового дня накатываются все изменения из юнита ночного ввода (писал ранее об этом).
Правда, опять крамола, мы не используем SQL для изменений в продуктовых таблицах. Наш стек позволяет напрямую работать с БД (SQL используется там, где нужный сложные выборки по нескольким таблицам и многим условиям, и то, там не так все просто с точки зрения производительности).
Строго говоря, для каждой продуктовой таблицы есть "опция ведения". Которая состоит из 4-х модулей:
Update модуль - собственно работа с таблицей и ее журналом. Чтение образа "до", запись образа "после" (с проверкой что "до" не изменился). В реальной жизни может быть достаточно сложным т.к. может работать не с одной, а с несколькими логически связанными таблицами.
Validate модуль - контроль валидности данных записи в соответствии с бизнес-логикой
Модуль интерактивной работы с записью (позволяет вносить ручные изменения). Вводим уникальный ключ - если запись есть - работаем режиме изменения, если нет - в режиме добавления. После внесенных изменений вызывается Validate модуль и, если ошибки нет - Update модуль.
Модуль внешнего ввода. Получает на вход образ "после", читает из таблицы образ "до" (опять, есть запись - изменение, нет - добавление), вызывает Validete модуль, если ошибок нет - Update модуль. Этот же модуль позволяет делать "накат по журналу". Для этого на вход передается не образ "после", а имя библиотеки где лежит журнал и ключ записи с образом "после". В этом случае образ "после" берется из журнала.
Данная схема кажется сложной, но стабильно работает в условиях очень больших нагрузок.
И все равно вопрос - вы начинате транзакцию на списание 300р со счета. И вэтот же момент начинается транзакция на списание 400р. А на счете вмего 500р. Каждая транзакция по отдельности валидна. Вместе - нет. Что делать?
Вообще, это основа. Прочитал запись, что-то в ней сделал, перед тем как записывать - проверь. Прочитай еще раз - не изменилось ли что-то пока ты работал с записью
И еще раз - что делать когда счета плательщика и аолучателя в разных банках?
Что делать, когда платкж по каким-то причинам отправлен на ручной контроль и подтверждение его будет через час, два а то и вообще только завтра?
Еще более сложное. В банке каждый день наступает фаза закрытия опредня и перехода на следующий день. Фактически - сведение баланса. Но при этом платежи принимаются и обрабатываются. Но не в том юните, где идет переход на следующий лень, а в другом - юните ночного ваода. А потом, когда основной юниттперешел на следующий день, в него накатываются все изменения из ночи... Как с этим быть?
Статья про сферического коня в вакууме, а не про реальную жизнь.
Вот поэтому и создается платежный документ, сумма ставится на холд, а потом уже платежный документ проверяется. И если он подтвержден, тога уже осуществляется сам перевод. Деньги при этом списываются с холда.
Если платкж заблокировпн, сумма с холда возвращается обратно.
Описывать технические проблемы в отрыве от бизеслогики в корне неверно
В обработке платежей это наименьшая из проблем
Плюс еще комплекс контроля платежей. Лимиты, комплаенс контроль...
И кроме внутренних платежей (оба счета в одном банке), есть входящие (из другого банка) и исходящие (в другой банк)
На счете текущий баланс и холды - суммы, которые еще не списаны (получение денег плательщиком не подтверждено), но уже "обещаны к списанию".
Плюс платежные документы, связанные с этим счетом
Не транзакции, а платежные документы. Платкльщик, счет плательщика, получатель, счет получателя, сумма платежа...
Очень похоже на попытку реализовать обычный динамический массив, но как-то не до конца...
Неочевидна стратегия расширения массива (сколько памяти выделяется когда емкость исчерпана и пытаемся добавить новый элемент?). Не видно итераторов. Не видно интерфейсов обращения к произвольному элементу массива...
Если это делалось специально под конкретную задачу, то хорошо бы описать что именно за задача, какие требования и сценарии использования всего этого.
Естественно. Если вместо простого if поставить вызов какой-то функции (с разворачиванием-сворачиванием стека, а еще, не дай бог, выбросом исключения), а потом вызвать все это 100500 раз, разница в производительности будет заметна. Но кого все это волнует? Главное же концептуальность. А если у потребителя все это начинает тормозить - пусть заменит один старый сервер на два новых, помощнее и подороже.
Зато когда MS заявляет что поддержка Win10 заканчивается, а для перехода на Win11 потребуется обновить комп, нас это дико возмущает - да как они посмели...
Сложные конструкции вообще сложно понимать :-) Абстракции плохи тем, что когда нужно понять логику работы программы (а в жизни неоднократно приходилось сталкиваться с весьма и весьма мудреной и неочевидной бизнеслогикой), приходится работать одновременно с несколькими исходниками, отслеживая каждый вызов "до самого дна".
А уж когда нужна аналитика типа "кто использует этот модуль" (на предмет оценки рисков и объема регресс-тестов при его изменении), а там начинается - этот модуль используется модулем А, который вызывается из модуля Б, а тот задействован в модуле В...
Но не суть.
Простота чтения кода - это не про использование или не использование if. Это про стиль. Многовложенные if, конечно ничуть не проще в понимании чем пирамида многоуровневых абстракций... Так что тут стоит использовать все средства, которые могут облегчить читаемость кода.