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
140 lines
13 KiB
Markdown
140 lines
13 KiB
Markdown
# Проверка членства в СРО: эксплуатация
|
||
|
||
Источник `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
|
||
стабилен между загрузками, включая переход `active` → `excluded`. 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. После разрешения владельца обязательна ограниченная проверка
|
||
фактической разметки перед первой полной загрузкой.
|