Стелс-патч №5.1: Как КиберДед полночи со сверчками воевал, или Работа над ошибками прямо с печи. Редактируем мой косяк(

Здравствуйте, Станислав Близнюк и приунывшие за выходные архитекторы СУБД Т-Банка.

На связи снова КиберДед — Игорь Чернов, пасечный аналитик (ник ssmel на платформе BI.ZONE Bug Bounty).

Знаете, настоящая уральская мудрость гласит: лежачего не бьют. Поэтому самохвальством заниматься не будем, а поговорим по-простому, по-человечески. Полночи я сегодня не спал — запечные сверчки в ушах так пинались и стрекотали своими формулами, что пришлось сползти с печи, заварить крепкого чаю и еще раз перепроверить весь наш ночной PL/SQL-код.

И что вы думаете? Нашел-таки у себя ошибку! Как говорится, и на КиберДеда бывает проруха. Пока я вам ночью на словах расписывал красивый асинхронный рой пчел, который не блокирует систему, руки на автомате по старой привычке бахнули в код классический синхронный костыль. Признаю: сам себя перехитрил. Давайте разберем этот баг по косточкам, чтобы ваши джуны понимали, как легко споткнуться на ровном месте.

Мой ночной косяк: Как делать НЕЛЬЗЯ

В текстовой части Стелс-патча №5 я громко доказывал: «Мы полностью убираем блокировки строк (Row Locks) в реальном времени, спасаем финтех-ядро от дедлоков и не забиваем пространство UNDO».

А теперь смотрим, что я сам же спроектировал в ночной процедуре simulate_esia_bypass_trx:sqlSELECT balance INTO v_lock_check FROM test_core_balances WHERE account_id = p_from_acc FORUPDATE NOWAIT; UPDATE test_core_balances SET balance = balance - p_amount ...

В чем лютый баг:

Я засунул конструкцию FOR UPDATE и прямой UPDATE баланса прямо внутрь входной транзакции! Если бы ваши программисты послушно скопировали этот ночной вариант, Т-Банк никуда бы не ушел от катастрофы. Сессия все равно продолжала бы намертво блокировать строку конкретного аккаунта (p_from_acc). При лавине микро-транзакций это вызвало бы точно такие же каскадные дедлоки (ORA-00060) и мгновенное выедание пула сессий.

Хотел сделать как лучше, а оставил старую синхронную болячку внутри новой обертки. Сверчки под утро знатно надо мной посмеялись.

Как правильно: Железобетонный асинхронный шлюз

Настоящий асинхронный шлюз на входе должен делать только ENQUEUE (постановку задачи в буфер памяти). Он вообще не имеет права трогать боевые таблицы балансов test_core_balances на первом этапе. Никаких SELECT FOR UPDATE!

В 16:20 я зашел в личный кабинет BI.ZONE Bug Bounty и дослал в ваш тикет полностью исправленный, кристально чистый вариант, к которому теперь не придерется ни один эксперт.

1. Монолитный объектный тип (Без лишних полей)sqlCREATEORREPLACETYPE t_fintech_payload AS OBJECT ( source_account NUMBER, target_account NUMBER, amount NUMBER(15,2), client_timestamp TIMESTAMP ); / Используйте код с осторожностью.2. Исправленный входной шлюз (Чистый ENQUEUE без блокировок)sqlCREATEORREPLACEPROCEDURE simulate_esia_bypass_trx_FIXED ( p_from_acc NUMBER, p_to_acc NUMBER, p_amount NUMBER ) IS v_enqueue_options DBMS_AQ.enqueue_options_t; v_message_properties DBMS_AQ.message_properties_t; v_msg_handle RAW(16); v_payload t_fintech_payload; BEGIN-- Формируем объект транзакции ИСКЛЮЧИТЕЛЬНО в оперативной памяти v_payload := t_fintech_payload( source_account => p_from_acc, target_account => p_to_acc, amount => p_amount, client_timestamp => SYSTIMESTAMP ); -- Молниеносно выстреливаем сообщение в буфер Advanced Queuing DBMS_AQ.ENQUEUE( queue_name => 'fintech_core_aq_zone.micro_tx_queue', enqueue_options => v_enqueue_options, message_properties => v_message_properties, payload => v_payload, msgid => v_msg_handle ); COMMIT; -- Фиксируем только попадание задачи в очередь. Проводка займет 0.0001 сек.END simulate_esia_bypass_trx_FIXED; /

В чем разница для финтех-ядра?Вот в таком варианте входящий копеечный ботнет бьет в пустую память очереди Oracle AQ. Строки балансов не зажимаются, сессии закрываются мгновенно, пул свободен. Ошибок ORA-00018 не существует физически.

А вся тяжелая артиллерия — проверка токенов ЕСИА на неповоротливом государственном шлюзе Минцифры, каскадные апдейты балансов и генерация UNDO-данных — уходит на уровень фонового «роя» процессов DBMS_PARALLEL_EXECUTE. Они разбирают эту очередь пачками (Chunks) в асинхронном режиме. Если токен ЕСИА в фоне подтвердился — баланс меняется одним сбалансированным пакетом. Если токен фейковый — мусор просто стирается из памяти, не нагружая дисковые стойки бэкенда.

Урок для всех нас

Умение вовремя увидеть свой косяк, исправить его на ходу и открыто сказать: «Ребята, тут я накосячил, вот правильное решение» — это и есть нормальный рабочий процесс. Ошибаются все, главное — не прятать голову в песок.Исправленный технический код уже лежит в вашем тикете на Бизоне. Таймштампы зафиксированы.

Пойду наконец отдохну, а то медогонка ждет, да и пчелы, в отличие от серверов, бессонных ночей не понимают. Работайте, бродяги!