Безопасный scope: это руководство применимо только к собственным или явно авторизованным QA-, staging- и production-средам. Описаны сценарии диагностики, тестирования и наблюдаемости вашей собственной CAPTCHA-интеграции — не для сторонних сайтов и не для несанкционированных workflow.
Один и тот же CAPTCHA-токен решается дважды — а поток CaptchaAI занят обоими вызовами одновременно. Так на практике выглядит цена проблемы session state в собственном worker-пуле: retry попадает на другой узел, который ничего не знает о прогрессе первого. Если task_id, токен и результат проверки живут только в памяти одного процесса, каждый перезапуск контейнера, каждый ретрай оркестратора и каждый второй worker в очереди рискуют запустить решение заново — вместо того чтобы переиспользовать то, что уже готово. Правильный ответ — не «более быстрый» worker, а общий store с TTL и идемпотентным ключом, который отвечает на вопрос «эта задача уже решалась?» до отправки нового запроса в CaptchaAI.
Из чего состоит CAPTCHA-состояние worker'а
Чтобы retry на любом узле вёл себя предсказуемо, состояние сессии должно опираться на четыре элемента, а не только на токен:
task_id, полученный от CaptchaAI при отправке задачи;- сам токен решения — действителен только в пределах TTL, который задаёт провайдер;
- результат проверки на вашем backend (
pass/fail) — хранится отдельно от токена, потому что валидный токен и подтверждённая верификация — не одно и то же; - метаданные запроса:
pageurl,sitekey,action(для типов, где он применим).
Смешивать эти четыре сущности в одном поле — частая причина, по которой worker не может однозначно понять, был ли конкретный запрос уже обработан.
Схема хранения в Redis
Redis — не единственный вариант (тот же принцип работает и с Memcached, и с таблицей в Postgres с индексом по идемпотентному ключу), но именно Redis даёт TTL «из коробки» и атомарные операции, которые нужны для конкурентного доступа нескольких worker'ов. Минимальная схема:
| Ключ | Поле | TTL |
|---|---|---|
captcha:{idem} |
task_id |
5 мин |
captcha:{idem} |
token |
TTL провайдера |
captcha:{idem} |
verified |
10 мин |
TTL здесь — не формальность: он ограничивает, как долго worker имеет право доверять кешу, прежде чем перепроверить состояние на backend.
Идемпотентный запрос на Python
Идемпотентный ключ строится по контексту конкретного запроса (например, ID регистрации или ID тестового сценария), и перед отправкой в CaptchaAI worker сначала проверяет, не решена ли задача уже кем-то другим:
import json, redis
r = redis.Redis()
def get_or_solve(idem: str, sitekey: str, pageurl: str) -> str:
cached = r.get(f'captcha:{idem}:token')
if cached:
return cached.decode()
token = solve(sitekey, pageurl)
r.setex(f'captcha:{idem}:token', 110, token)
return token
Если ключ уже занят другим worker'ом, функция вернёт кешированный токен вместо повторного вызова solve() — именно это и предотвращает дублирование задач при ретрае. TTL в 110 секунд выбран с запасом относительно типичного окна валидности токена: worker успевает переиспользовать его, но не рискует отправить backend-у токен, который провайдер уже считает истёкшим.
Безопасные правила хранения
- идемпотентный ключ строится по контексту запроса и никогда не переиспользуется между разными пользователями или сессиями;
- токены не сохраняются дольше TTL провайдера — просроченный токен в кеше опаснее, чем его отсутствие;
- результат backend-верификации хранится отдельно от токена, а не подменяет его;
- в логах пишите только префикс токена (первые символы), а не значение целиком — полный токен в логах не нужен даже для внутренней отладки.
Логи, метрики и пример по нагрузке
Структурированные логи помогают сравнивать поведение CAPTCHA между релизами и быстро находить регрессии в собственных формах:
import json, time, logging
log = logging.getLogger('captcha-qa')
def record(event: str, **fields) -> None:
payload = {'ts': time.time(), 'event': event, **fields}
log.info(json.dumps(payload, ensure_ascii=False))
Минимальный набор полей для каждой попытки: slug, captcha_type, task_id, wait_seconds, verify_status, env. Этого достаточно, чтобы построить дашборд медианы / P90 / P99 по типу CAPTCHA и по среде — и увидеть, растёт ли доля повторных решений после очередного релиза оркестратора.
Пример по нагрузке
Команда разворачивает 12 worker'ов в Kubernetes-кластере (европейский регион хостинга — типичный выбор для СНГ-команд) и обрабатывает около 5000 регистрационных форм в час. Пиковая одновременная нагрузка требует 40–50 занятых потоков CaptchaAI — это укладывается в тариф ADVANCE ($90/мес, 50 потоков, безлимит решений на поток; тарификация именно по потокам, а не по решению).
Без идемпотентного ключа в Redis всплеск ретраев на дефлопнувшем поде легко удваивает число фактически отправленных в CaptchaAI задач: по счёту это может быть незаметно (потоки те же), но задержка конкретной формы для пользователя вырастает ощутимо.
Если в структурированные логи попадают персональные данные пользователей, отдельно проверьте состав полей — требования 152-ФЗ «О персональных данных» и GDPR предполагают, что в логах остаются только данные, которые вы вправе обрабатывать, а не полный дамп запроса.
Типичные проблемы
| Симптом | Что сделать |
|---|---|
| Дубли задач CaptchaAI | Проверьте, что идемпотентный ключ действительно уникален для каждого сценария |
| Токен невалиден на ретрае | Снизьте TTL ключа token ближе к реальному окну провайдера |
| Redis недоступен | Откатывайтесь к локальному решению per-worker, не блокируйте пайплайн целиком |
| Конкурентный доступ нескольких worker'ов | Используйте SETNX или Lua-скрипт вместо GET+SET вручную |
QA-чек-лист перед выкладкой
- Запрос отправляется только на собственные или авторизованные endpoints.
- Тестовые учётные записи, события и платежи помечены как фиктивные.
- CAPTCHA-токен проверяется на собственном backend, а не доверяется клиенту.
- Логи содержат
task_id, тип CAPTCHA, время ожидания и pass/fail — без полного значения токена. - TTL идемпотентного ключа согласован с интервалом ретраев в оркестраторе (Kubernetes, Ansible), а не выбран наугад.
- Скрипт возвращает корректный exit code, чтобы CI мог принять решение.
FAQ
Почему один и тот же CAPTCHA-токен решается дважды подряд?
Обычно потому, что состояние хранится только в памяти одного процесса. Retry, который попадает на другой узел или в новый под, ничего не знает о первой попытке и отправляет задачу заново. Идемпотентный ключ в общем сторе с TTL закрывает этот разрыв.
Какой TTL ставить для CAPTCHA-токена в Redis?
Чуть меньше реального окна валидности токена у провайдера — в примере выше это 110 секунд. Слишком длинный TTL приводит к тому, что backend получает уже просроченный токен; слишком короткий сводит на нет смысл кеширования.
Что делать, если Redis временно недоступен?
Не блокируйте весь пайплайн: переключайте worker на локальное хранение состояния для текущей задачи и логируйте деградацию отдельным событием, чтобы её было видно на дашборде.
Можно ли применять этот подход на сторонних сайтах?
Нет. Описанные сценарии применимы только к собственным или явно авторизованным средам. Для чужих ресурсов нужно письменное разрешение владельца — без него это не QA-тестирование, а несанкционированный доступ.
Безопасные связанные руководства
- Быстрый старт CaptchaAI
- QA-тестирование CAPTCHA в авторизованных средах
- Тестирование CAPTCHA API на собственных формах
- Отладка: браузерный тест падает, а API проходит
- reCAPTCHA v2 через API
- Cloudflare Turnstile через API
- GeeTest v3 через API
Готовы навести порядок в распределённом CAPTCHA-пайплайне? Подключите CaptchaAI.