Files
mostovik-backend/docs/sro-membership-source.md
Aleksandr Meshchryakov 18971d33ec
All checks were successful
Mostovik Backend CI/CD / Tests and lint (push) Successful in 3m55s
Mostovik Backend CI/CD / Build linux/amd64 release images (push) Successful in 3m43s
Mostovik Backend CI/CD / Deploy and verify internal main (push) Has been skipped
Mostovik Backend CI/CD / Deploy customer main (push) Has been skipped
Mostovik Backend CI/CD / Deploy dev (push) Successful in 1m45s
feat: complete published registry contracts and gated SRO ingestion
2026-09-14 17:01:02 +02:00

13 KiB
Raw Blame History

Проверка членства в СРО: эксплуатация

Источник sro_membership_check, группа/тип sro_membership, карточка sro-membership-check. Upstream — сайт reestr-sro.ru; внутренний справочник организаций используется для точной привязки UID и ОКПО. Другие внешние реестры загрузчик не опрашивает.

Доступ и пределы запросов

По умолчанию сбор закрыт. Для открытия необходимы одновременно SRO_UPSTREAM_ACCESS_APPROVED=true и непустой SRO_UPSTREAM_APPROVAL_REFERENCE — ссылка/номер документированного разрешения владельца сайта на автоматизированные query/search запросы. Согласование разработки внутри проекта не заменяет разрешение владельца upstream. Условия разрешения должны быть совместимы с настройками загрузчика; более строгие пределы задаются перед включением. Секреты в approval reference хранить нельзя.

Оба ручных entrypoint проверяют этот gate до enqueue; task и HTTP клиент повторяют проверку перед внешним IO. Закрытый gate возвращает typed 409 upstream_access_not_approved. Если разрешение отозвано после постановки, job завершается с ошибкой, без успешной пустой публикации.

Настройка По умолчанию Значение
SRO_REQUEST_INTERVAL_SECONDS 3 Минимум 3 секунды между запросами, включая redirects/retries; можно увеличить.
SRO_HTTP_MAX_RESPONSE_BYTES 2097152 Предел распакованного тела одного ответа.
SRO_HTTP_TIMEOUT_SECONDS 30 Read timeout; connect timeout 10 секунд.
SRO_MAX_REQUESTS_PER_RUN 10000 Общий предел запросов одного запуска, включая redirects/retries/sitemaps.

HTTP клиент делает не более трёх попыток при транспортной ошибке и ответах 429/500/502/503/504. Retry-After учитывается как секунды или HTTP date, ожидание свыше 300 секунд завершает запуск ошибкой. Цепочка redirects ограничена пятью ответами; каждый адрес проверяется до обращения. Разрешён только HTTPS без credentials/нестандартного порта, на reestr-sro.ru и его поддоменах. Переменные окружения HTTP proxy автоматически не используются. HTML/query с идентификаторами не выводится в application logs.

Публикация и обновление

Celery task: parsers.sro_membership_check.refresh; Python entrypoint: apps.parsers.tasks_registry_snapshots.parse_sro_membership, keyword args requested_by_id=None, mode="incremental"|"full".

Проверяются только организации текущего канонического справочника. Инкрементальный запуск выбирает новые организации и те, у которых изменились name/full_name, ИНН/ОГРН/ОКПО. Изменение определяется fingerprint; у модели Organization нет универсального updated_at. Полный запуск проверяет весь справочник.

Lookup использует точный ОГРН, при его отсутствии/неподходящем формате — ИНН с проверкой нормализованного имени. Если нет пригодного идентификатора, запуск отклоняется с sro_candidate_identifier_missing, без удаления прежних записей. Ответ дополнительно сопоставляется по ИНН+ОГРН через внутренний индекс. Отсутствие ОКПО, конфликт идентификаторов, неоднозначная организация, неизвестный статус, неполные обязательные поля или missing/unsafe SRO URL отправляют строку в quarantine. Новые организации из внешнего ответа не создаются.

Одна запись соответствует паре canonical organization UID + SRO ID. Record UID стабилен между загрузками, включая переход activeexcluded. SRO ID/URL берётся из same-site ссылки или точного ID, разрешённого по same-site sitemap; по одному похожему названию URL не угадывается. Для даты допуска читается same-site страница members по ИНН. Неизвестная дата остаётся null с причиной not_found, sro_page_unresolved или parse_error. Наблюдаемая дата реестра не подменяется датой загрузки.

Raw ответы сохраняются в приватном ZIP artifact с manifest URL→файл; отдельные staged rows содержат source fields, нормализованный payload и причину quarantine. Для SRO используется отдельный FileSystemStorage: PARSER_PRIVATE_ARTIFACT_ROOT по умолчанию равен <repository-root>/private-parser-artifacts, вне MEDIA_ROOT. Путь обязан быть абсолютным и не находиться внутри MEDIA_ROOT, в том числе после разрешения symlink. Ошибка конфигурации запрещает файловую операцию. Приватный storage не выдаёт публичный URL; файлы создаются с правами 0600, каталоги 0700. Остальные источники сохраняют прежний default_storage.

В контейнерном окружении задайте PARSER_PRIVATE_ARTIFACT_ROOT=/app/private-parser-artifacts и подключите к web/worker один постоянный volume по этому пути. Каталог должен быть доступен пользователю приложения для записи и не должен монтироваться в public media, static или reverse-proxy document root. Существующая retention-команда удаляет private файлы через тот же artifact.file.delete(); удаление/пересоздание контейнера не должно удалять volume. Миграция 0037 меняет storage в состоянии модели без перемещения данных. До этой версии живой SRO сбор не запускался. Если оператор ранее создал SRO artifacts в старом MEDIA_ROOT вручную, их требуется отдельно перенести в private root с сохранением относительных имён и убрать публичные копии до открытия источника; автоматического fallback на public raw нет.

Публикация записей, checkpoint организаций, load log и terminal job выполняется в одной транзакции. Инкрементальная публикация заменяет данные только успешно проверенного набора организаций; записи остальных организаций сохраняются. Transport/schema failure или отменённая job откатывает публикацию и checkpoints. Допустимый пустой ответ удаляет старые членства проверенной организации. Любая семантически некорректная строка остаётся в quarantine и отклоняет весь запуск с sro_incomplete_membership_scan. Это сохраняет прежние членства и checkpoints: неизвестный статус или неразрешённая ссылка не доказывают, что членство исчезло. Организация остаётся кандидатом следующего incremental запуска.

Если incremental не нашёл изменившихся организаций, batch имеет нулевую дельту и наследует source registry date/version последней успешной публикации; ссылка на неё сохраняется в metadata.base_snapshot_artifact_id. Данные не переписываются.

После commit безопасно ставится существующая задача обновления общих export artifacts. Ошибка брокера после commit не превращает успешную публикацию в failure; готовность выгрузки отражает собственный контракт export service.

Счётчики и диагностика

artifact.published_count, result.published_records_count и load_log.records_count содержат размер текущего опубликованного набора источника. Для incremental это общий результат, включая неизменённые организации. metadata.snapshot_records_count и snapshot_organizations_count дают такие же общие record/distinct organization counts; batch_published_records_count и updated_records_count — число опубликованных строк текущей дельты. parsed_count, quarantine и candidate/queried counts относятся к текущей проверке; складывать их с общим published count нельзя.

Источник публикует весь собственный справочник; карточка/list/dashboard для трёх новых источников используют весь published scope. Coverage ОПК показывается отдельно. МСП и бюджет продолжают полную замену: новый scoped аргумент публикации по умолчанию None сохраняет их поведение.

Для расследования используют job status/error, artifact status, metadata.rejection_reason, rejection_reasons и staged disposition/reason. Они содержат ограниченные коды ошибок, не raw HTML. Partial raw ZIP сохраняется при ошибке уже начатого сбора; предыдущая публикация остаётся доступна. Приватные raw artifacts удерживаются минимум 90 дней и минимум 10 последних успешных SRO batches; неуспешные batches не вытесняют эти десять успешных.

Расписания и граница проверки

Миграция parsers.0036_sro_disabled_schedules создаёт выключенные schedules: daily incremental в 04:00 Europe/Moscow; monthly full 16-го числа в 04:00 Europe/Moscow — после наблюдаемого обновления реестра 15-го. Перед включением нужно подтвердить фактическую периодичность/условия разрешения. Расписание само не открывает gate; оба механизма по умолчанию выключены.

На 14.09.2026 query/search и живой SRO snapshot не запускались. Основная страница без query доступна через read-only web; свежий robots.txt получить не удалось. Указанные в source-first спецификации Crawl-delay: 3 и query/search disallow учтены консервативно. DOM selector/mapping проверены синтетическими fixtures формы из docs/source-integration/sources/sro-membership-check/backend-api.md, а не полным live scan. После разрешения владельца обязательна ограниченная проверка фактической разметки перед первой полной загрузкой.