Cache gerenciado

O componente Cache da Zenifra usa Valkey atualmente e foi projetado para dados que podem ser reconstruídos a partir de uma fonte de verdade. O armazenamento é descartável: reinícios, expiração e eviction podem remover entradas sem representar perda de dados da aplicação.

Padrão cache-aside

  1. Leia a chave no Valkey.
  2. Em caso de miss, consulte a fonte de verdade.
  3. Grave o resultado com TTL e, quando possível, um pequeno jitter.
  4. Invalide a chave quando a fonte de verdade mudar.
GET catalog:product:42
SET catalog:product:42 <json> EX 300
DEL catalog:product:42

Nunca armazene tokens, senhas ou dados regulados sem uma política de proteção adequada. Use namespaces, limite o tamanho dos valores e monitore a taxa de hit/miss.

Node.js e TypeScript

npm install iovalkey
import Redis from 'iovalkey'
import { randomInt } from 'node:crypto'

async function loadProductFromSource() {
  return { id: 42, name: 'example' }
}

const url = new URL(process.env.VALKEY_URL ?? '')
const client = new Redis({
  host: url.hostname,
  port: Number(url.port),
  username: decodeURIComponent(url.username),
  password: decodeURIComponent(url.password),
  tls: { servername: url.hostname },
})

const key = 'catalog:product:42'
const cached = await client.get(key)
const value = cached ?? JSON.stringify(await loadProductFromSource())
if (!cached) await client.set(key, value, 'EX', 300 + randomInt(0, 30))
console.log(value)
await client.quit()

Substitua loadProductFromSource() pela consulta real da aplicação. O exemplo usa jitter para reduzir expirações simultâneas; em alto volume, combine-o com lock curto ou request coalescing.

Python

python -m pip install valkey
import json
import os
import random
from urllib.parse import urlparse
from valkey import Valkey

def load_product_from_source():
    return {"id": 42, "name": "example"}

url = urlparse(os.environ["VALKEY_URL"])
client = Valkey(
    host=url.hostname, port=url.port, username=url.username, password=url.password,
    ssl=True, ssl_check_hostname=True, decode_responses=True,
)

key = "catalog:product:42"
cached = client.get(key)
value = cached or json.dumps(load_product_from_source())
if cached is None:
    client.set(key, value, ex=300 + random.randint(0, 30))
print(value)
client.close()

Substitua load_product_from_source() pela consulta da aplicação. Para evitar cache stampede, apenas um worker deve reconstruir uma chave em expiração; os demais podem aguardar ou usar um valor stale por uma janela curta.

O que não fazer

  • Não trate Cache como armazenamento permanente.
  • Não use TTL infinito para dados que podem crescer sem limite.
  • Não faça KEYS * para limpar o cache; use namespaces e SCAN ou invalidação por chave.
  • Não deduza que um hit significa que o dado está atualizado; defina uma política de invalidação.

Operação segura

Escolha TTLs de acordo com a velocidade de mudança da fonte, não com o tamanho do plano. Um TTL muito curto aumenta carga e latência; um TTL muito longo deixa dados obsoletos. Jitter aleatório evita que milhares de chaves expirem no mesmo segundo. Quando uma reconstrução for cara, use um lock curto com token e permita que leitores recebam um valor stale por uma janela controlada.

Monitore hit rate, miss rate, latência, erros de serialização e quantidade de evictions. Um aumento de misses pode indicar invalidação excessiva ou capacidade insuficiente, enquanto um hit rate alto não prova que o conteúdo está correto. Defina uma forma de invalidar por entidade após escritas na fonte, e trate falha do Cache como um caminho normal: a aplicação deve consultar a origem e continuar funcionando sem os dados descartáveis.

Próximos passos

Nessa página