Pular para o conteúdo principal

Cache operations (playbooks)

Esta página é o manual de operações pro cache da plataforma. Cada seção é um playbook independente — entrar, fazer, sair.

Para entender por que o sistema é assim, veja Cache architecture. Para a visão de "como funciona, sem detalhes técnicos", veja Cache strategy.

Pré-requisitos

  • Acesso ao repositório cactus-agents/front-ops no GitHub
  • Permissão pra rodar Actions workflows
  • Em casos críticos: acesso ao Cloudflare Dashboard (CT e/ou BT)

Todos os exemplos abaixo assumem state77-com como environment. Substitua pelo seu caso — a lista viva de environments é front-ops/config/brands/*/environments/, e é ela que popula o dropdown do purge.

Os workflows de cache do front-ops

WorkflowTriggerPra que serve
Manual Cache PurgemanualPurge cirúrgico por tag/glob/segmento numa env (playbooks 1-4)
Cache StatusmanualInspeção read-only do estado do cache (playbook 5)
Purge Allmanual ou chamado por outro workflowPurge total de uma env em "two-pass reverse-order"
Bump cache generationmanualRotaciona o token de generation da frota inteira (playbook 11)
Purge Monitorcron */5 * * * *Detecta drift de shape do BFF e dispara Purge All sozinho
Sync purge optionspush em main + cron diárioAbre PR regenerando os dropdowns do Manual Cache Purge

:::info Por que aparece uma PR automática quando você adiciona uma env ou tag Os dropdowns environment e tag_quickpick do manual-cache-purge.yml são YAML estático — o GitHub não os popula do filesystem ao renderizar a UI. O sync-purge-options.yml roda a cada push em main que toca config/brands/*/environments/**, config/cache/defaults.yml ou config/cache/service-api-defaults.yml (mais um cron diário às 09:00 UTC) e abre uma PR quando os dropdowns divergem da realidade. Mergeie a PR, ou o purge da env nova não fica selecionável. :::

Inputs do Manual Cache Purge

InputDefaultNota
environment— (obrigatório)Dropdown com todas as envs. Regenerado pelo sync-purge-options.yml
tag_quickpick(none — use tags/glob below)Dropdown curado de tags. Preenche tags quando tags está vazio
tagsvazioCSV livre (ex: payments,providers). Vence o tag_quickpick quando preenchido
globvazioGlob de tag (ex: games:*). Mutuamente exclusivo com tags
segmentsvazioCSV de resource keys (avançado). Vazio = purgeAll
scopeplatform-cacheplatform-cache / service-api / zone / all
dry_runfalseResolve e imprime a config sem chamar a API

:::caution O default de scope é platform-cache, não all Todo playbook abaixo que pede scope: all exige que você mude o dropdown. Se você deixar no default, só o brand worker é purgado — service-api e zone ficam intactos, e o sintoma volta. :::


Playbook 1 — Invalidar config de brand (banners, payment-providers)

Quando usar: mudança no backoffice (banner novo, ordem de payment-providers, feature flag) que não está aparecendo após 1-2min.

Tempo: ~30s.

Passos:

  1. GitHub → cactus-agents/front-ops → Actions → Manual Cache Purge → Run workflow
  2. Inputs:
    • environment: state77-com (ou prod-7k-bet-br, etc)
    • tag_quickpick: brand (ou tags: brand)
    • scope: all ← mude do default platform-cache
  3. Run
  4. Aguarda ~30s. O step summary mostra resposta de cada camada.
  5. Valida visitando o site em janela anônima (pra contornar browser cache).

O que acontece por trás:

  • Brand worker engine.purgeByTag("brand") invalida brandConfig (e qualquer outro resource com tag brand)
  • Cascade pro service-api purga entradas com tag brand (/v2/bff/features, /v2/appearance, /v2/bookmaker-settings, /v2/configurations/casino, /v2/country)
  • Próxima request → BFF → dado fresco

Por que isso é suficiente:

  • TTL do brandConfig é 60s; este purge é só pra forçar antes do TTL natural expirar
  • Não precisa zone purge nem CF Cache Reserve

Playbook 2 — Invalidar catálogo de jogos

Quando usar: jogo novo adicionado, jogo removido, mudança de ordem em categorias.

Tempo: ~30s.

Passos:

  1. Actions → Manual Cache Purge → Run workflow
  2. Inputs:
    • environment: state77-com
    • glob: games:* (cobre games:list, games:detail, games:wins, games:stats, games:high-payers, games:category, games:provider, games:base, games:listPage, games:all, games:home, games:votes)
    • scope: all
  3. Run.

Variante (mais cirúrgica):

Pra atingir só home + casino (não detalhe de jogo):

  • tag_quickpick: rows
  • scope: all

Pra atingir só a estatística "pagou hoje":

  • tag_quickpick: stats
  • scope: all

O vocabulário completo de tags está no dropdown tag_quickpick — ele é gerado a partir das tags declaradas em config/cache/defaults.yml e config/cache/service-api-defaults.yml.


Playbook 3 — "O site inteiro tá com dados velhos, quero zerar tudo"

Quando usar: ultima opção. Postmortem em andamento, problema generalizado, ou após uma mudança grande no BFF.

Tempo: ~1-2min (incluindo zone purge).

Passos:

  1. Actions → Manual Cache Purge → Run workflow
  2. Inputs:
    • environment: state77-com
    • (deixa tags/glob/segments vazios) = purgeAll
    • scope: all
  3. Run.

O que acontece:

  • platform-cache engine.purgeAll() no brand worker
  • service-api purga TODAS as entries com o originDomain da env
  • CF zone purge_everything (apaga TODO o cache de zone — assets, HTMLs, .data)

Cuidado: zone purge_everything tem custo CF e é nuclear. Use só quando realmente necessário.

Variante mais agressiva: o workflow Purge All faz um two-pass reverse-order purge — várias rodadas, camadas em ordem inversa, sem set -e (todas as etapas tentam mesmo que uma falhe). É o que o Purge Monitor dispara automaticamente em drift, e o que o cutover pós-warm do deploy roda. Use quando o purge simples não convergiu.


Playbook 4 — Invalidar só uma camada (debug)

Quando usar: investigando se um problema específico está em uma camada ou outra.

Inputs:

  • scope: platform-cache — só invalida no brand worker (memory + CF Cache API + KV snapshot do platform-cache). É o default.
  • scope: service-api — só invalida no proxy worker (KV API_CACHE_KV + CF Cache API do service-api)
  • scope: zone — só CF zone purge (edge cache do SSR HTML)

Use cases típicos:

  • "Dados que vêm da home tão velhos, mas detalhe tá certo" → scope: platform-cache, tags: rows
  • "Pagamentos errados no checkout" → scope: service-api, tags: payments
  • "Página /alguma-coisa tá com HTML antigo mesmo após /api/cache/purge" → scope: zone

Playbook 5 — Diagnosticar staleness

Quando usar: "tá dando velho mas eu não sei onde", antes de chutar um purge.

Tempo: ~10s.

Passos:

  1. Actions → Cache Status → Run workflow
  2. Inputs:
    • environment: state77-com
    • include_state: true (default — probe live state, mais lento)
    • tag / glob: opcionais, pra filtrar
  3. Run.
  4. Abrir o step summary. Vai mostrar uma tabela com:
    • Qual resource está warm em CF Cache API
    • Quantos segundos de age (desde a última população)
    • Quantos segundos fresh sobram antes de virar stale
    • Quais tags cada resource tem

Interpretação:

  • Primary hit ❌ em vários resources → cache está cold, próxima request vai pagar custo de BFF
  • Age > TTL mas Fresh? ⚠️ → resource está stale, SWR vai revalidar na próxima request
  • Resource esperado não aparece → não está declarado na policy (BYPASS silencioso). Veja Playbook 7.

:::tip Antes de chutar um purge Rode o Manual Cache Purge com dry_run: true primeiro. Ele resolve e imprime exatamente qual env, quais tags e quais camadas seriam atingidas, sem chamar nenhuma API. Custa 10s e evita um purge_everything acidental. :::


Playbook 6 — Inspeção direta via curl

Pra automação, scripts, ou quando o GitHub Actions tá fora:

# Variáveis (substituir)
TARGET=state77.com
SECRET=$FRONT_BT_CACHE_PURGE_SECRET # a conta CF da env — state77-com é BT
WORKER_KEY=$FRONT_BT_CF_WORKER_KEY

# 1) Inspecionar policy declarada
curl -s "https://$TARGET/api/dev/cache-policy" \
-H "X-Cache-Secret: $SECRET" | jq .

# 2) Inspecionar estado live (com TTLs restantes)
curl -s "https://$TARGET/api/cache/inspect?include=state" \
-H "X-Cache-Secret: $SECRET" | jq .

# 3) Inspecionar só uma tag
curl -s "https://$TARGET/api/cache/inspect?tag=brand&include=state" \
-H "X-Cache-Secret: $SECRET" | jq .

# 4) Purge por tags
curl -s -X POST "https://$TARGET/api/cache/purge" \
-H "X-Cache-Secret: $SECRET" \
-H "cf-worker-key: $WORKER_KEY" \
-H "Content-Type: application/json" \
-d '{"tags": ["brand", "catalog"]}' | jq .

# 5) Purge tudo da brand (incluindo service-api)
curl -s -X POST "https://$TARGET/api/cache/purge" \
-H "X-Cache-Secret: $SECRET" \
-H "cf-worker-key: $WORKER_KEY" \
-H "Content-Type: application/json" \
-d '{}' | jq .

:::caution Envs atrás de CF Access Nos stage-* o hostname fica atrás do Cloudflare Access e esses curl não passam sem um service token. Nesses envs o deploy.yml costuma ter skip_post_deploy_purge: true justamente por isso — e a invalidação real vem da rotação de CACHE_GENERATION a cada deploy. :::


Playbook 7 — Adicionar resource novo ao platform-cache

Quando usar: novo serviço/módulo precisa cachear dados do BFF de forma agregada.

Tempo: ~15min, incluindo PR review.

Passos:

  1. Define o resource no front-ops/config/cache/defaults.yml:

    myNewResource:
    enabled: true
    ttlSeconds: 600 # 10min
    staleWhileRevalidateSeconds: 300
    staleIfErrorSeconds: 86400
    fallbackEnabled: false # ou true se for hot
    storage:
    primary: cache_api
    snapshot: none # ou kv
    keyPrefix: myNewResource
    tags: ["catalog", "my-domain"]
  2. Wire-up no service do brand worker:

    // app/services/my-new.cache.server.ts
    export async function getMyData(deps: Deps) {
    const result = await engine.fetch(
    "myNewResource",
    () => fetchFromBff(deps),
    { waitUntil: deps.ctx?.waitUntil },
    );
    return result.data;
    }
  3. (Opcional) Adicione ao service-api se o BFF path precisar de cache no proxy também.

    Em front-ops/config/cache/service-api-defaults.yml, dentro do bloco rules::

    rules:
    /v2/my/bff/path:
    ttl: 1800
    methods: ["GET"]
    tags: ["catalog", "my-domain"]

    As tags são o que conecta as duas camadas: é assim que o manual-cache-purge passa a mesma tag pro brand worker e pro proxy.

  4. PR em front-ops com as duas mudanças. Se você adicionou uma tag nova, o sync-purge-options.yml vai abrir uma segunda PR atualizando o tag_quickpick.

  5. Deploy explícito do brand worker. Um push no front-ops não redeploya o brand worker — o worker lê PLATFORM_CACHE_POLICY_JSON em deploy-time. Rode o deploy manualmente (ou aguarde o próximo deploy de código).

  6. Valida rodando Cache Status — o novo resource deve aparecer na tabela.

Atenção:

  • Resource user-scoped? Use fetchWithKey(resource, userId, fetcher) e adicione tags: ["user-scoped"] pra documentar. Nunca use fetch() sem suffix em resource per-user.
  • KV ON (fallbackEnabled: true + snapshot: kv) é caro (KV writes custam $). Use só pra resources hot ou pra cross-DC warm-up crítico.

Playbook 8 — Deploy não invalidou cache esperado

Sintoma: após deploy, mudança no código não aparece no site.

Diagnóstico:

  1. Verifica o run do deploy.yml. Procura por:
    • Skip purge notice (when configured) — se apareceu, o env tem skip_post_deploy_purge: true e nenhum purge por segmento rodou (comportamento esperado nesse env)
    • Purge platform cache after deploy — status 200?
    • Purge SSR cache (CF zone purge) — status 200?
    • Warm up cache — todos 200/304?
    • Probe served build id (observational orphan check) — o buildId servido bate com o deste deploy? Se não, há versão órfã atendendo tráfego
  2. Se algum step falhou: re-roda Manual Cache Purge com scope: all.

Causas comuns:

  • FRONT_{XX}_CACHE_PURGE_SECRET mismatch entre worker e workflow → 401 no step
  • cf_zone errado em deploy.yml → zone purge skip (warning)
  • Token CF sem permissão Cache Purge:Purge → 403
  • Env atrás de CF Access com skip_post_deploy_purge: true → purge nunca roda por design
  • Versão órfã do Worker servindo parte do tráfego → ver Verify single-version deployment

Veja config/brands/README.md no front-ops pro checklist completo de tokens CF.


Playbook 9 — Resource em BYPASS silencioso

Sintoma: dev adicionou engine.fetch("foo", ...) mas o resource não está cacheando.

Diagnóstico:

  1. Roda Cache Status na env.
  2. Procura foo na tabela.
  3. Se não aparece, é BYPASS — não está na policy.
  4. Logs do worker (wrangler tail) deveriam mostrar uma vez:
    [platform-cache] resource "foo" has no policy entry — running in BYPASS mode

Solução: PR no front-ops/config/cache/defaults.yml (Playbook 7).


Playbook 10 — Forçar invalidação direta no Cloudflare (último recurso)

Quando usar: workflows do GH down, ou cache.purge endpoint do brand worker quebrado.

Tempo: ~2min.

Passos:

  1. Cloudflare Dashboard → Zone (ex: state77.com) → Caching → Configuration
  2. Purge by URL se sabe exatamente o que zerar, ou
  3. Purge Everything se é nuclear.

OU via API:

# Zone ID (lookup)
curl -s -X GET \
"https://api.cloudflare.com/client/v4/zones?name=state77.com&account.id=$ACCOUNT_ID" \
-H "Authorization: Bearer $CF_API_TOKEN" | jq '.result[0].id'

# Purge everything
curl -s -X POST \
"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"purge_everything": true}'

Atenção: isso afeta SÓ o CF zone cache (SSR HTML em edge). Não toca platform-cache nem service-api. Pra purgar app-level use o Manual Cache Purge.


Playbook 11 — Rotacionar a cache generation da frota

Cenário: uma mudança de schema do BFF fez com que payloads cacheados em KV/CF Cache fiquem com formato incompatível com a nova versão do código — e você precisa que todas as camadas de todos os workers passem a usar um namespace novo e vazio.

Como funciona:

Existe um único token de generation. Ele é o prefixo de todos os namespaces de cache de longa duração da frota:

CamadaNamespace derivado
Brand worker — platform-cache (CF Cache API)platform-cache-<token>
Brand worker — api-client (LocalStorageCache no browser)api-cache-<token>
front-service-api — KV md5 prefix + CF Cache API<BUILD_KEY>-<token>

Trocar o token torna toda entry anterior inalcançável (não deletada — ela é evicted por LRU/TTL naturalmente).

Fonte de verdade operacional: front-ops/config/cache/generation.yml, campo current. O arquivo mantém o histórico de todos os bumps com data e motivo.

:::danger Não edite generation.yml à mão, e não edite as constantes do core As constantes em front-cactus-core/packages/platform-cache/src/generation.ts (DEFAULT_CACHE_GENERATION), packages/api-client/src/cache.ts (DEFAULT_CACHE_KEY_PREFIX) e front-service-api/src/index.ts (DEFAULT_GLOBAL_VERSION) são fallbacks in-source, usados só quando nenhum valor é injetado por env var. O próprio comentário do arquivo do core pede pra deixá-las quietas e usar o workflow. Bumpar essas constantes por PR não é o caminho — exige publicar pacote novo e não afeta o service-api já deployado. :::

Passos:

  1. Actions → Bump cache generation → Run workflow

  2. Inputs:

    InputO que faz
    reasonobrigatório. Vai pro commit message, pro histórico do generation.yml e pro step summary. Escreva algo que faça sentido em 3 meses
    target_valueopcional. Vazio = auto-incrementa o sufixo numérico do valor atual
    brand_workersnone / single / all — quais brand workers redeployar depois do bump
    single_environmentslug da env, quando brand_workers: single
    redeploy_service_apidefault true — dispara o deploy do front-service-api em CT e BT
  3. Run. O workflow computa o próximo valor, commita generation.yml (com o PAT FRONT_GH_ACTIONS_TOKEN) e dispara os deploys escolhidos.

  4. Aviso pré-deploy ao time: vai ter cold-start massivo. Faça em horário de baixo tráfego.

:::info Todo deploy já rotaciona a generation O step Patch wrangler.toml with runtime vars monta o valor efetivo como <generation.yml::current>-<BUILD_ID>. Como BUILD_ID são os 12 primeiros chars do SHA deployado, cada deploy já entra num namespace novo. Ou seja: para invalidar o cache de um worker, um deploy normal basta.

Este playbook só é necessário para o caso cross-deploy, frota inteira — quando você quer que workers que não vão ser redeployados também mudem de namespace, ou quando quer flipar o service-api junto.

Existe ainda um terceiro caminho, automático: em envs com run_warm_cutover: true, o deploy bumpa o secret CACHE_GENERATION_SECRET do worker (sem rebuild) logo depois do collector de warm, e então roda o purge-all multi-camada. :::

Quando NÃO fazer: se um purge normal (playbooks 1-3) ou um simples redeploy resolveria. Rotação de generation é última opção.


Sumário rápido — escolha o playbook certo

SituaçãoPlaybook
Backoffice mudou e não reflete1
Jogo novo não aparece2
"Tá tudo errado, zera tudo"3
Debug — quero saber em qual camada está o problema4, 5
Inspeção rápida via curl6
Adicionar resource novo7
Deploy não invalidou esperado8
engine.fetch("foo") não cacheia9
Workflow GH fora, preciso resolver via CF direto10
Mudança de schema do BFF quebrou caches da frota inteira11