Технический разбор · FuelProcessing

Марафон 22–24.06 — инженерная версия

Корневые причины, фиксы, MR'ы и пайплайны по каждому инциденту. Прод: контейнер fuel / БД fuel_prod1 на oktane, домен lk.oilonline.ru, репозиторий developers/FuelProcessing (proj 52), раннер fuel_octane.

6MR на проде
3в ревью/в работе
10ВИНК с таймаутом
762 849строк миграции
Времена МСК · восстановлено по транскриптам рабочих сессий
← Для людейДля разработчиков
Поставки

Карта MR и деплоев

MRВетка / сутьКлючевые местаПайплайнСтатус
!786fix/tatneft-proxy-oktane
хардкод адреса посредника → appsettings
UrlManager.TryInit(): 91.190.156.76141.101.196.65#8775прод · 22.06
!790 / !791per‑vink фильтр тех‑лимита (корень дублей)CardOwnerLimitsService.GetFields() + LimitService#8794прод · 22.06
!792fix/rosneft-category-header-zero-phantom
guard от phantom‑zero
hasPositiveTypeLimit в GetFileds + sync‑guard в RosneftApiService + 2 регресс‑теста#8810 / #8811прод · 23.06 11:50
merge 700ed6ae
!795fix/rosneft-transaction-timeout
анти‑зависание всех ВИНК
ReadWriteTimeout для RestSharp‑клиентов (×10) + StartNow() на ApiVinkJob, 9 файлов#8843прод · ~00:07 (24.06)
!789limitsForOptimize (a.okosten)
дедуп + sync‑state
бизнес‑ключ {CardId,LimitType,FuelCategoryId,FuelTypeId}; ApiLimitInfo.CreatedAtUtc/SentAtUtc; миграция AddApiLimitInfoSyncDates#8847 / #8848прод · ~02:30 (24.06)
backfill 762 849
!799feat/web-activity-logging
сквозной HTTP‑трейс
FuelProcessing.WebLogging · decorator LoggingRestClient → Loki; ротация docker‑логов json-file 100m×5masterпрод · ~04:50 (24.06)
!794RosneftLimitSend Information → Warning (видимость egress в Kibana)1 файл, уровень логированияв ревью
!787fix/es-sink-durable-buffer
durable ES‑sink + креды из ENV
Serilog ES sink, durable bufferв ревью
fix/gpn-limit-dedup-antiwipe
дедуп на стороне ГПН
GazpromLimitService (цикл набора лимитов без дедупа)CI / review
Корни и фиксы

Разбор по инцидентам

Роснефть Phantom‑zero: «0» поверх реального лимита
Симптом

Жалоба: лимиты не срабатывают по А1353 (карта 7826010121736033), в кабинете gDT=0 при намерении >0. Расхождение зеркало↔кабинет — до 16 240 из 19 640 карт Роснефти (93%).

Корень

CardOwnerLimitsService.GetFileds() отдавал owner‑level тех‑лимит с FuelTypeId=null, InDay=0 без per‑vink фильтра. Эта «фантомная» нулевая категория уходила в кабинет поверх валидного типа топлива и обнуляла лимит.

Фикс — MR !792

Guard hasPositiveTypeLimit (проверка InDay/InWeek/InMonth) в GetFileds + sync‑guard в RosneftApiService: не отправлять 0‑категорию, если по типам есть положительные значения. 2 регресс‑теста.

Верификация на проде
# после деплоя #8811 (23.06 ~11:50 МСК) тестовый INSERT TaskLimit (карта 97371, ДТ=600) → guard пропустил, кабинет обновился EditCardLimit: 1552 за 15 мин → 396 → 40 → тишина # автореконсиляция вредных нулей: 0 · 4 новые карты (300/300/300/600) — корректно с первого прохода

Темп самолечения ~9 карт/мин; острая жалоба закрыта. Полный свип ~19 640 карт — фоном.

Роснефть Заморозка синхры на 3,5 часа
Симптом

С 15:19 МСК встали транзакции Роснефти/Лукойла/Вездехода. GetOperByContract по ISS233681 завис на чтении тела ответа (68 start / 67 finish).

Корень

RestSharp 106 на .NET5: Timeout=80000 ограничивает только получение заголовков, ReadWriteTimeout не задан → чтение тела фактически бесконечно. Зависший поток через [DisallowConcurrentExecution] заморозил общий джоб HistoryTransactions.

Фикс — MR !795

ReadWriteTimeout + CancellationToken для всех 10 ВИНК‑клиентов (Rosneft/Gazprom/Lukoil/Tatneft/Vezdehod/NatCar/Track/TaifNK/Inforkom/S), try/finally в ApiJob, и StartNow() на триггере ApiVinkJob — джоб стартует сразу после деплоя, а не ждёт 35‑мин тик. Локальная сборка net5 чистая (SDK 10 + rollForward=latestMajor).

Связь с требованием

Совпало с гипотезой про динамическую генерацию отдельного джоба на каждый ВИНК — изоляция «одни винки не стопают других».

Роснефть Дубли лимитов и фантомные нули (22.06)
Симптом

СТК‑75 / договор 9 (А1353): карты с противоречивыми лимитами (например 501л + 0л), фантомные строки, ~3089 дублей на 535 картах.

Корень

CardOwnerLimitsService.GetFields() возвращал тех‑лимит per‑owner‑type без фильтра по ВИНК → размножение. Отдельно — минимальный лимит 0 выставлялся при тех‑лимите (логика isOpen).

Фиксы

MR !790/!791 (per‑vink фильтр), временный guard TEMP-ROSNEFT-FREEZE (обратимая заморозка очереди, 431 задача), затем динамические per‑vink джобы, увеличение pool size, чистка фантомов и бэкфилл. Финальный канонический дедуп — MR !789 (бизнес‑ключ + миграция ApiLimitInfo).

Пойманный «дубль» на карте 7005830021056304 (3 копии LimitId=419497 за 12с) после анализа оказался легитимным фан‑аутом справочника ГПН (GetCustomLimits по FuelTypeVink), а не багом. Реальные orphan‑дубли (301 + старый 1500) — ~1% карт, наследие VESTAFUEL‑1947.
ГПН · Лайт Скай Сверка и восстановление лимитов
Запрос

Проверить лимиты ГПН для ООО «Лайт Скай» (Companies.Id=393, договор DirectContracts.Id=2298 «Э1», вендор DCWV=18), подготовить восстановление. Заданный порядок: сначала дубли, потом восстановление; приоритет — корректные лимиты в системе.

Что сделано

Снят бэкап в проде: Limits_lightsky_bak_20260623 (15 690 строк), ApiLimitInfo_lightsky_bak_20260623 (25 417). Написан и развёрнут на oktane инструмент ~/projects/fuel-gpn-lightsky/dryrun/lsky_db_resync.py — режимы compare / apply / classify, 3‑сторонняя сверка (наша БД сейчас / снимок / кабинет ГПН), read‑only по умолчанию.

Ключевое инженерное решение

Источник истины — таблица Limit в БД, не LimitVinkHistory: снимки истории устаревшие (значения расходятся с текущими в разы), слепая заливка из истории создала бы финансовые дубли. Восстановление — через штатный воркер (AddLimitsToCards → AddLimit) после устранения дублей, а не прямым re‑push.

По egress

Прямой скрипт‑отправка / ssh‑туннель / реверс‑прокси на несколько IP — отклонено: ОПТИ‑24 банил oktane именно за множественные входы с неверными кредами с одного egress (бан 18.06, снят 22.06). Мульти‑IP обходит IP‑бан, но не per‑account throttle.

Татнефть «Мёртвый» узел посредника
Корень

Адрес index‑0 прокси (91.190.156.76) был захардкожен в UrlManager и не отвечал — десятки запросов улетали в мёртвый хост.

Фикс — MR !786

Вынос адреса в appsettings, переключение на живой узел oktane (141.101.196.65), деплой #8775. Контейнер tatneftproxy:latest поднят на oktane (:6868, AllowedIPs включает 172.17.0.1). Обходной egress‑релей ГПН через kakao демонтирован — прямой канал восстановлен по всем 4 IP.

Наблюдаемость Web Activity Logging + дашборд
Запрос

Настроить сбор логов (скрейпер) с привязкой к Loki и ротацией, проверить поток логов, оформить MR и слить; отдельный дашборд web‑activity.

Сделано — MR !799

Новый проект FuelProcessing.WebLogging: decorator LoggingRestClient поверх RestSharp‑клиентов всех API (Gazprom/Tatneft/Lukoil/Track), JSON‑сериализация запрос/ответ + маскирование секретов → Loki. Ротация docker‑логов json-file max-size=100m ×5 на всех 4 контейнерах. Grafana‑дашборд web-activity на oktane.

Контекст: egress к Роснефти (внешний SDK Rosneft.API) писал payload только в stdout, а ES‑sink transactionhandler молчит с июня (индекс ...-2026-06 отсутствует). Отсюда — потребность в сквозном трейсе (!799) и durable‑буфере (!787, в ревью).

В разработке · смежный инструмент

fuel-architect — доменные эксперты по коду

Внутренний веб‑инструмент для поддержки/доработки FuelProcessing: интерактивная карта архитектуры (127 узлов / 101 связь / 11 доменов / 51 Quartz‑job) + команда LLM‑экспертов по доменам, которые читают реальный код репозитория и предлагают diff‑правки. Backend на 127.0.0.1:8731 (uvicorn / systemd fuel-architect.service). Инвариант: граф и эксперты обязаны соответствовать живому коду (детерминированный drift‑gate).

ДоменЭкспертЗона / модель
Лимитыfuel-limits-expertTaskLimits, ApiLimitInfo, синк через ВИНК · kimi-k2.7-code
Блокировкиfuel-blocks-expertTaskBlock, CardBlock, BlockStatus · kimi-k2.7-code
ВИНК‑интеграцииfuel-vink-expertIApiService + 11 вендор‑адаптеров · kimi-k2.7-code
Транзакцииfuel-transactions-expertFinanceTransaction, RabbitMQ · kimi-k2.7-code
Биллингfuel-billing-expertBalance, Overdraft, ContractBalance · kimi-k2.7-code
Картыfuel-cards-expertIssuingCard, CardGroup, CardLog · kimi-k2.7-code
Контрактыfuel-contracts-expertContract, Discount, TermTemplate · kimi-k2.7-code
АЗСfuel-stations-expertGasStation, FuelType, маршруты · kimi-k2.7-code
Платформаfuel-platform-expertEF Core, DataContext, Quartz, миграции · kimi-k2.7-code
Дашбордfuel-dashboard-expertReport, FuelOnline, Pumping · qwen3-coder-next
Мониторингfuel-monitoring-expertAlerting, Serilog, Elasticsearch · qwen3-coder-next

Все вызовы через единый openai.AsyncOpenAI → LiteLLM‑прокси :4000. Оркестратор (свод нескольких экспертов) → glm-5.2; reviewer / adversarial‑верификация / fallback → deepseek-pro. Наблюдаемость — Arize Phoenix (phoenix.vidak.wellsoft.pro, проект fuel-architect), недоступность Phoenix запрос не роняет.

Возможности

Реконсилятор дрейфа · verify.py

Сверяет задекларированную модель (data/*.json) с реальным деревом FuelProcessing: UNION job‑классов из 3 Quartz‑конфигов (без хардкода «52»), 11 ВИНК‑адаптеров, owns_paths‑глобы, context_pack file:line. Ненулевой код + diff при расхождении → CI drift‑gate.

Карта доменов · /api/domain-graph

Cytoscape‑граф 127 узлов с фильтром по домену и 1‑hop соседями; взвешенные cross‑domain рёбра + топ‑10 bridge‑узлов (золотая граница). Мини‑мета‑граф 11 доменов, FTS5‑поиск.

Безопасный read→diff · sandbox + propose_diff

Чтение только внутри owns_paths‑глобов домена + path‑traversal guard (realpath). propose_diff: hash‑сверка исходника, валидация whatthepatch dry‑run, провенанс модели. Автозаписи в репо нет — аппрув руками.

4 режима ревью

scan_domain (прочёс домена → findings file:line + severity), review_file, propose_diff, adversarial_verify (независимая перепроверка находки вторым экспертом → вердикт real/hallucination).

Статус по фазам

ФазаНазваниеСтатус
1Fidelity & Data Truthготова · 23.06
2LiteLLM Gateway + Phoenixготова · 23.06
3Bug Review & Clear Mapготова · 24.06
4Memory & Context (FTS5 memory.db)в работе · 2/4 планов
5Acceptance Eval & Coverage ≥80%не начата

174 теста (27 добавлено в последней сессии). Ожидают браузерного UAT: визуальная кластеризация графа, мета‑граф, E2E с живым LLM («Найти баги» → findings → diff → adversarial‑вердикт). Phase 5 — ground‑truth eval на баге «фантом cat=0» (worktree на коммите 407b7cf04^, сверка с реальным фиксом).

Артефакты

Инструменты и бэкапы

lsky_db_resync.py

compare/apply/classify, 3‑сторонняя сверка, read‑only по умолчанию. На oktane, ~/projects/fuel-gpn-lightsky/dryrun/.

Бэкапы в fuel_prod1

*_lightsky_bak_20260623 (Limits 15 690, ApiLimitInfo 25 417); TaskLimits_freeze_bak_20260622.

GSD‑воркспейсы

fuel-rosneft-remediation и fuel-gpn-lightsky в ~/projects/*/.planning/ — ROADMAP/REQUIREMENTS/STATE.

Тест‑стенд

VESTAFUEL‑1943: свежий стенд fueltest, прогон тестов, приведение Jira‑тест‑кейсов в порядок.

Хвосты

Открытые задачи (тех.)