Шлюзы агентов стоят между ненадёжными клиентами и дорогими побочными эффектами. В 2026 году зрелые кластеры OpenClaw требуют ключей идемпотентности, чтобы повторы не приводили к двойным списаниям. Сочетайте атомарные claim в Redis с проверкой JSON Schema, дренажом nginx, бюджетами 429 и Retry-After и диагностикой таймаутов, затем переходите к разделам ниже.
Контракт и нормализация
Используйте один канонический транспорт: либо заголовок Idempotency-Key, либо поле JSON idempotency_key. Отклоняйте ключи с энтропией после base64url меньше 16 байт и ограничивайте длину 128 ASCII символами, чтобы логи и балансировщики оставались предсказуемыми. Удаляйте пробелы, запрещайте управляющие символы, затем считайте SHA-256 и используйте только хеш в Redis. Каждая операция должна быть привязана к кортежу (tenant_id, route_id, key_hash, schema_version), чтобы обновление схемы намеренно ломало старые повторы.
Зафиксируйте в спецификации, что один и тот же ключ не может переиспользоваться при изменённом теле запроса: при несовпадении хеша тела возвращайте 409 с машиночитаемым кодом, чтобы клиенты не молча перезаписывали платежи. Для gRPC-стиля продублируйте правила в proto-комментариях и в генерации клиентов, иначе команды мобильной разработки увидят только HTTP-вариант и начнут расходиться с бэкендом.
Redis и атомарность
Первый запрос выполняет SET idempo:{tenant}:{hash}:claim NX EX 172800; только успешный воркер идёт в апстрим. Остальные читают сохранённый конверт. После успеха сохраняйте HTTP-статус, безопасное подмножество заголовков и контрольную сумму тела BLAKE3 или SHA-256. Если тело больше 1 MiB, выгружайте его в объектное хранилище, оставляя в Redis указатель.
# псевдо OpenResty
local key = "idempo:" .. tenant .. ":" .. idem_hash
if redis:set(key..":claim", pid, "NX", "EX", 172800) == nil then
return replay(key)
end
proxy_pass http://openclaw_upstream;
Матрица топологий
| Топология | Плюсы | Минусы | Когда |
|---|---|---|---|
| LRU в процессе | Минимальная задержка | Расщеплённый мозг | Локальная разработка |
| Кластер Redis | TTL и NX зрелые | Горячие ключи | Боевой стандарт |
| SQL advisory lock | Аудит | Больше задержки | Регулируемый финтех |
| etcd аренда | Интеграция mesh | Сложность эксплуатации | Корпорации с mesh |
Окна TTL 172800/259200 с
Сорок восемь часов совпадают с практикой платёжных API. Семьдесят два часа покрывают ночные сверки, которые займают третий календарный день. После истечения TTL новый запрос — это новая операция; SDK должны вращать ключ, когда пользователь явно выбирает «повторить как новое». Юридические SLA, маркетинговые тексты и значение EX в Redis должны совпадать, иначе аудиторы найдут расхождение.
Архивируйте истёкшие конверты в холодное хранилище, если комплаенс требует многолетнего следа, а горячая память не выдерживает. Маркируйте архивы версией схемы и тегом Git шлюза.
Конверт повтора
Повторы должны быть побайтово идентичны, насколько это возможно. Если gzip даёт недетерминизм, храните канонический JSON до сжатия и сжимайте на выдаче. Для потоковых ответов инструментов нумеруйте чанки, чтобы частичный поток не считался завершённым. Если доля несовпадений контрольных сумм превышает 0,01 % десять минут подряд, эскалируйте как регрессию сериализации или сжатия.
Добавьте в конверт поле schema_version и gateway_build, чтобы при отладке сразу видеть, какая комбинация валидаторов сформировала ответ. При миграции между версиями protobuf или JSON явно увеличивайте номер версии схемы, даже если внешний API не менялся: внутренние поля часто меняются незаметно для клиентов, но критичны для идемпотентности.
Храните отметку времени записи конверта в UTC и отдельно фиксируйте часовой пояс источника, если клиенты глобальные; это быстро упрощает расследования.
Связка с nginx
Завершайте TLS на периметре, нормализуйте ключи и передавайте внутренний доверенный заголовок с производной хеша в OpenClaw. Во время дренажа держите один и тот же DSN Redis, чтобы «спящие» мобильные клиенты не теряли контекст. Установите proxy_read_timeout чуть выше внутреннего бюджета апстрима, согласуясь с рекомендациями по таймаутам. Исключите внутренние повторы из жёстких лимитов limit_req, чтобы не наказать здоровый трафик.
Нумерованный чеклист
- Пометить все мутирующие маршруты флагом
requires_idempotency=trueв каталоге сервисов. - Заставить SDK по умолчанию выпускать UUIDv4 и отвергать ключи короче 22 символов base64url.
- Иметь в продакшене минимум три первичных шарда Redis с AOF
everysec. - Написать интеграционные тесты с двадцатью параллельными дубликатами и ровно одним побочным эффектом.
- Провести учение: заменить 50 % подов во время активного дренажа при потоке дубликатов.
- Опубликовать коды ошибок для истёкшего ключа, несовпадения тела и неверного арендатора.
- Отображать долю повторов, p99 claim и число выселенных ключей в минуту.
Телеметрия
Счётчики idempo_claim_total, idempo_replay_total, idempo_conflict_total с хешированными бакетами арендаторов ограничивают кардинальность меток. Разделяйте RTT Redis и задержку модели, чтобы деградация кэша не выглядела как «тормозит LLM». Если доля повторов выше 35 % более тридцати минут, сначала ищите клиентский цикл. Еженедельный обзор конфликтов помогает рано ловить интеграторов.
В платёжных шлюзах разделяйте «идемпотентный хит» и «фактируемое событие», иначе маржа завысится. Клиентские панели могут показывать скользящее среднее за семь дней для проактивной поддержки.
Дополнительно полезно строить гистограммы длительности claim отдельно для «холодного» и «тёплого» кэша ключей: всплеск холодной ветки часто означает, что клиенты начали генерировать новые ключи на каждом ретрае вместо повторного использования. Сравнивайте эти гистограммы с показателями очередей провайдера, чтобы доказать на инцидент-брифинге, что проблема не в шлюзе, а в SDK партнёра. Экспортируйте в SIEM только хеши и идентификаторы корреляции, без сырых ключей.
Для мультиарендных установок заведите отдельный бюджет памяти на арендатора: если один клиент исчерпывает лимит конвертов, изолируйте его префикс, не останавливая весь кластер. Публикуйте внутреннюю «карту риска» маршрутов, где идемпотентность критична (платежи, удаление данных, массовая рассылка), и обновляйте её после каждого релиза инструментов.
Наконец, свяжите метрики идемпотентности с трассировкой OpenTelemetry: span «claim» должен быть родителем для span «upstream», чтобы в UI сразу видеть, был ли ответ реальным вычислением или повтором из Redis. Это сокращает время расследования инцидентов с часов до минут.
Добавьте отдельный счётчик «холодных» ключей, которые впервые появились в окне пять минут: его всплеск почти всегда означает, что клиенты перестали переиспользовать ключи при ретраях. Сопоставляйте его с очередями провайдера, чтобы на мостике доказать, что шлюз не источник задержки.
Для мультиарендных площадок заранее определите квоту памяти на арендатора и сценарий изоляции, если один арендатор исчерпал лимит конвертов.
Экспортируйте еженедельный PDF с версией Lua, версией схемы и топ-5 кодов отказов — аудиторы просят именно такую связку «конфиг плюс метрики» и быстрый доступ к логам корреляции по идентификатору запроса и метке времени.
Партиции
При падении Redis явно выберите fail-closed или fail-open. Деньги требуют 503 и указаний по backoff. Клиентам — не более пяти попыток с джиттером и потолком 32 секунды, если только Retry-After не больше, см. материал про 429. Ведите компенсационный журнал с тем же хешом, если апстрим успешен, а конверт не записался.
Региональные сбои требуют документировать, какие заголовки входят в хеш тела; иначе мобильные прокси добавят кеш-заголовок и вызовут ложные конфликты. Ежеквартальный chaos-эксперимент с искусственной задержкой Redis выявляет горячие регрессии.
Держите однострочный шаблон постмортема: хеш, маршрут, версия схемы и арендаторы.
FAQ
Нужны ли ключи для GET?
Обычно нет; не помещайте запись в GET.
Один ключ на разные маршруты?
Нельзя — идентификатор маршрута обязателен.
Очередь офлайн?
Выдавайте ключ при постановке в очередь и переиспользуйте при сбросе.
Хосты Mac mini на Apple Silicon тихие и подходят для круглосуточных репетиций: локальные Redis, nginx и бинарник OpenClaw ведут себя как в продакшене. macOS даёт те же launchd, Keychain и песочницу, что снижает разрыв между ноутбуком и ЦОД. Аренда облачного Mac mini у MacHTML стоит около 16,9 USD в сутки, что дешевле ночного инцидента с двойными списаниями.
На том же хосте можно гонять openssl s_client и сверять цепочки сертификатов, чтобы nginx и шлюз оставались согласованы перед релизом.
Репетируйте дедуп на облачном Mac
Арендуйте Mac mini, склонируйте Redis и nginx, прогоните дубликаты через OpenClaw и только потом расширяйте TTL в бою.