Pular para o conteúdo
~/.primo-academy.sh
☰ Aulas · Edge Deploy Frontend: build, runtime, Cloudflare Workers, Vercel Edge, image opt, preview envs, atomic deploys · 0/7
Recomendado: essencial

Atomic deploys e cache: blue/green, cache headers, invalidation, rollback

5 min de leitura

fonte

Voce fez deploy, app quebrou em prod, corre pra reverter. Mas "reverter" demora 30min: build antigo, rollback, DNS switch, esperar propagar. Esse no cobre atomic deploys e cache: como deploy sem downtime (blue/green), cache headers corretos, invalidation estrategica, e rollback instantaneo.

Atomic deploy = nova versao substitui a antiga como unidade atomica. User sempre ve versao consistente (v1 OU v2, nunca mix). CDN + cache = 95% do TTFB vem de cache, 5% do origin. Cache strategy errada = user ve versao antiga apos deploy (cache stale) ou cache nunca funciona (TTFB alto).

Voce sai de "deploy = subir codigo e rezar" pra "deploy = zero downtime

  • cache strategy".

O essencial 🟢

Os 3 problemas de deploy "nao- atomic".

  1. Mix de versoes. User recebe index.html (v2) e app.js (v1). App quebra (HTML novo espera JS novo).
  2. Cache stale. User recebe versao antiga de CDN mesmo apos deploy novo.
  3. Rollback lento. "Reverter" = git revert + redeploy + esperar propagar (15-30min).

A solucao: atomic deploy. Nova versao inteira substitui a antiga. User sempre ve versao consistente. CDN/load balancer faz swap instantaneo (DNS, load balancer, ou feature flag).

Decision tree: cache strategy por tipo de conteudo. Regra: (1) Static asset COM hash = cache 1y (immutable), (2) HTML = cache 0s + must-revalidate, (3) API = cache 60s com stale-while-revalidate, (4) User-specific = no-store. SEMPRE use content-hash em JS/CSS pra evitar stale. Atomic deploy garante zero mix de versoes.

Cache-Control headers - a base.

# Static asset COM hash no filename
Cache-Control: public, max-age=31536000, immutable
# (1 ano - pode ser cacheado pra sempre, hash garante freshness)

# HTML / SSR pages
Cache-Control: public, max-age=0, must-revalidate
# (sempre revalidar com servidor, CDN checa ETag)

# API response (GET, idempotente)
Cache-Control: public, max-age=60, stale-while-revalidate=300
# (60s fresh, 300s serve stale enquanto revalida)

# User-specific (auth)
Cache-Control: private, no-store
# (NAO cachear, user-specific)

# Mutation (POST/PUT/DELETE)
Cache-Control: no-store
# (NAO cachear response)

max-age = segundos ate o cache expirar. stale-while- revalidate = serve stale enquanto revalida em background (latencia minima).

ETag - "cache fingerprint".

ETag: "abc123"
Cache-Control: public, max-age=0, must-revalidate

Server manda ETag. Browser guarda. Proximo request: If- None-Match: "abc123". Server responde 304 Not Modified (sem body) se ETag bate = economia de bandwidth.

Vary - "cache por criterio".

Vary: Accept-Encoding, Accept-Language

CDN cachea variantes por Accept-Encoding (gzip vs br), Accept-Language (en vs pt-BR). Mesma URL, respostas diferentes por header.

Content-hash em filename - "evita stale".

// Build gera:
app.a3b4c5.js  (hash do content)
app.b9c2d1.css

// HTML referencia:
<script src="/app.a3b4c5.js"></script>

// Deploy novo:
app.e7f8g2.js  (novo hash)
app.h4i5j6.css

// HTML referencia:
<script src="/app.e7f8g2.js"></script>

// Browser:
- v1: <script src="app.a3b4c5.js"> - cached
- v2: <script src="app.e7f8g2.js"> - novo request

Hash = cache busting automatic. CDN serve v1 forever (ate expirar em 1 ano). v2 e' novo request. Sem mix.

Vite, Webpack, esbuild fazem isso automatic. Configure:

// vite.config.ts
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        entryFileNames: "assets/[name].[hash].js",
        chunkFileNames: "assets/[name].[hash].js",
        assetFileNames: "assets/[name].[hash][extname]",
      },
    },
  },
});

Atomic deploy com Vercel/Cloudflare Pages.

Vercel faz atomic por default:

  • Cada deploy = novo immutable deployment.
  • DNS switch instant.
  • Rollback = 1 click no dashboard (volta pro deployment anterior).
  • Sem downtime.

Cloudflare Pages similar: cada push = novo deployment, swap instant, rollback via dashboard.

Blue/green deployment.

- Blue: versao atual (v1), recebe 100% trafego.
- Green: nova versao (v2), recebe 0% (staging).
- Deploy: green pronto, switch DNS/load balancer.
- Switch: 100% trafego pra green.
- Rollback: switch de volta pra blue.

Zero downtime, rollback em segundos. Use com feature flag pra canary: 5% trafego pra green primeiro, monitor, scale up.

Canary deployment - "5% primeiro, depois 100%".

1. Deploy v2 como canary (5% trafego).
2. Monitor error rate (Sentry), latency, business metrics.
3. Se OK: scale 25% → 50% → 100%.
4. Se bad: rollback automatico (volta 100% pro v1).

Canary = blue/green com gradual rollout. Use feature flag ou load balancer com weight adjustment.

Cache invalidation strategies.

  1. Time-based (max-age): Automatic. Cache expira em N segundos. Use pra: API responses, dados que mudam periodicamente.

  2. Tag-based (Cache-Tag header

    • purge API): Tag arquivos/ responses, purge via API. Use pra: blog posts, produtos com update.
  3. Content-hash (filename): Cache nunca invalida - novo arquivo = novo request. Use pra: JS/CSS/assets.

  4. Surrogate-Key (Fastly/Cloudflare Enterprise): Tag + purge global. Use pra: multi-CDN.

Cache no Cloudflare.

// Cloudflare Workers - cache API
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext) {
    const cache = caches.default;
    const cached = await cache.match(request);
    if (cached) return cached;

    const response = await fetch(request);
    const cacheable = new Response(response.body, response);
    cacheable.headers.set("Cache-Control", "public, max-age=3600");
    ctx.waitUntil(cache.put(request, cacheable));
    return response;
  },
};

Cloudflare cache = edge cache global. Cache HIT = response do edge, sem ir ao origin.

Vercel - purge cache.

# Vercel CLI
vercel cache purge --path=/api/products

Ou via dashboard: Settings > Data Cache > Purge.

Cache-Control em Next.js.

// next.config.js
async headers() {
  return [
    {
      source: "/api/:path*",
      headers: [
        { key: "Cache-Control", value: "public, max-age=60, stale-while-revalidate=300" },
      ],
    },
    {
      source: "/_next/static/:path*",
      headers: [
        { key: "Cache-Control", value: "public, max-age=31536000, immutable" },
      ],
    },
    {
      source: "/:path*",
      headers: [
        { key: "Cache-Control", value: "public, max-age=0, must-revalidate" },
      ],
    },
  ];
}

3 rules: API (60s), static (1y), pages (revalidate).

CDN cache vs browser cache.

  • Browser cache: user device. Por URL, por origin. Persistente.
  • CDN cache: edge location (Cloudflare, Vercel). Por URL, por edge. Persistente (ate TTL).
  • Origin cache: server (seu app). In-memory ou disk.

2 layers de cache (browser + CDN) = 95% das requests nao chega ao server. Custo reduzido 10x.

Atomic deploy + cache strategy = "deploy sem dor".

1. Build: gera app.<hash>.js
2. Test: roda E2E em preview
3. Atomic deploy: envia build pra CDN
4. CDN: invalida HTML (1 URL)
5. HTML novo: referencia app.<hash>.js (novo)
6. Browser: baixa HTML novo, descobre app.<hash>.js, baixa
7. CDN serve app.<hash>.js (cache miss 1x, depois hit)
8. Rollback: 1 click no Vercel, volta pro deployment anterior

Zero downtime, rollback instant, sem stale.

Aprofundamento 🟡

Surrogate-Key / Cache-Tag. CDN enterprise (Cloudflare, Fastly) tem tag-based purge:

// Cloudflare
response.headers.set("Cache-Tag", "product-123,category-electronics");

// Purge
await fetch(`https://api.cloudflare.com/.../purge_cache`, {
  method: "POST",
  body: JSON.stringify({ tags: ["product-123"] }),
});

1 purge = todas as URLs com aquela tag invalidadas. Use pra: CMS publica novo conteudo, atualiza produto, etc.

Service Worker + cache strategy.

// service-worker.ts
self.addEventListener("fetch", (event) => {
  if (event.request.destination === "image") {
    event.respondWith(
      caches.open("images-v1").then((cache) =>
        cache.match(event.request).then((cached) => {
          return (
            cached ||
            fetch(event.request).then((response) => {
              cache.put(event.request, response.clone());
              return response;
            })
          );
        })
      )
    );
  }
});

Service Worker = cache custom no client. Stale-while-revalidate: serve cache, atualiza em background. Use pra: PWA, offline-first.

Cache poisoning - "cache entrega conteudo errado". Risco: se Vary header nao setado, CDN cache user A response e serve pra user B. Solucao: sempre Vary: Cookie, Authorization em response user-specific.

ETag vs Last-Modified.

  • ETag: hash do content. Mais preciso (1 byte change = novo ETag). Use por default.
  • Last-Modified: timestamp. Menos preciso (1s granularity).

Use ETag por default. Some servers (S3) usam ambos - preferem ETag.

Negative caching. Cache de "404 Not Found" = economizar requests repetidos pra URLs que nao existem. Use com cuidado: Cache-Control: public, max-age=60 em 404. Ataque: atacante faz 10K requests pra URLs inexistentes = cache poisoned.

Layered cache.

- Browser cache (max-age=31536000, immutable) - 90% hit
- CDN cache (Cloudflare, max-age=86400) - 5% hit (após browser miss)
- Origin (server) - 5% reach

3 layers = 99% cache hit. Origin = backup only. Custo de origin drasticamente reduzido.

Cache hit ratio (CHR). CHR = % de requests servidos do cache. CHR 95% = excelente. CHR 80% = aceitavel. CHR < 70% = investigue.

Ferramentas: Vercel Analytics, Cloudflare Analytics, browser DevTools Network tab (size from cache).

Rollback strategies.

  1. Vercel dashboard: 1 click (rollback).
  2. Cloudflare Pages: dashboard ou API.
  3. Custom CI: git revert commit + redeploy.
  4. Feature flag: toggle off (instant, no deploy).
  5. Blue/green: switch DNS pra blue (instant).

Feature flag rollback e' mais rapido (30s) que redeploy rollback (5-15min). Use feature flag pra componentes criticos.

Zero-downtime migration. Migrar DB sem downtime:

  1. Adicionar nova coluna (v2) sem remover v1.
  2. Codigo escreve em ambas (dual-write).
  3. Backfill v2 com dados v1 (background job).
  4. Codigo le de v2 (com fallback pra v1).
  5. Codigo para de escrever em v1.
  6. Drop v1 (cleanup).

6 steps = zero downtime, rollback em qualquer step.

Leitura recomendada:

Dica: o erro mais comum em deploy e' "cache stale" (user ve versao antiga apos deploy). 3 fixes: (1) content-hash em filename (Vite/Webpack fazem automatic), (2) HTML sempre revalidate (max-age=0, must-revalidate), (3) CDN purge HTML (NAO assets com hash). Sem isso = user ve JS/CSS novo + HTML antigo = app quebrado. **Atomic deploy

  • cache strategy correta** = zero dor.

No proximo no (ultimo), vamos projeto final: deploy de SPA + edge function com preview por PR, ISR, image optimization, e atomic deploys. O "hello world" de apps em producao moderna.

// Quiz

Qual a diferenca entre 'atomic deploy' e 'incremental deploy', e por que atomic e' melhor para zero downtime?

Escolha uma alternativa

// recursos

// avaliação da trilha

—
ainda sem avaliações