Cache
O pacote oferece uma implementação robusta de cache usando Redis, com suporte a tipos genéricos e TTL (Time To Live) configurável. É especialmente útil para armazenar temporariamente dados frequentemente acessados, reduzindo a carga em outros serviços como banco de dados.
Componentes Principais
1. Inicialização
Para configurarmos a aplicação para utilizar cache, a instrução abaixo deve ser incluída na função main
.
// Inicialização do cache
cacheDB.Initialize()
Características da inicialização:
- Conexão automática com Redis.
- Integração com OpenTelemetry para monitoramento.
Variáveis de ambiente necessárias:
CACHE_URI
: host do banco de dados.CACHE_PASSWORD
: porta do banco de dados (Opcional).
2. Criação de Cache
// Criação de um novo cache com TTL
cache := NewCache[MinhaEstrutura]("nome-do-cache", time.Hour)
Parâmetros:
name
: Nome único para identificar o cache.ttl
: Tempo de vida dos dados no cache (time.Duration).
3. Operações Principais
Armazenamento (Set)
// Armazenando um único item
usuario := Usuario{Id: 1, Nome: "João"}
err := cache.Set(ctx, usuario)
// Armazenando múltiplos itens
usuarios := []Usuario{
{Id: 1, Nome: "João"},
{Id: 2, Nome: "Maria"},
}
err := cache.Set(ctx, usuarios)
Recuperação (Get)
// Recuperando um único item
usuario, err := cache.One(ctx)
// Recuperando múltiplos itens
usuarios, err := cache.Many(ctx)
Deleção (Del)
// Removendo dados do cache
err := cache.Del(ctx)
Recursos Avançados
1. Tipagem Genérica
O cache utiliza tipos genéricos do Go, permitindo forte tipagem:
// Definição de estrutura
type Usuario struct {
Id int
Nome string
}
// Cache tipado para Usuário
userCache := NewCache[Usuario]("usuarios", time.Hour)
// Cache tipado para outro tipo
pedidoCache := NewCache[Pedido]("pedidos", 30 * time.Minute)
2. Observabilidade
- Monitoramento via OpenTelemetry.
- Logging estruturado de operações.
- Rastreamento de erros.
3. Gerenciamento de Conexão
- Suporte a cluster Redis.
- Fechamento seguro de conexões.
Exemplos de Uso
1. Cache com Banco de Dados
O módulo de banco de dados já tem uma integração transparente com o cache.
func BuscarUsuario(ctx context.Context, id int) (*Usuario, error) {
// Criar cache com 1 hora de duração
cache := cacheDB.NewCache[Usuario]("usuarios_cache_key", time.Hour)
// Ao executar a query, o mecanimos interno do método 'NewCachedQuery' realiza os seguintes passos
// 1 - Valida se existe um registro no cache para a chave informada.
// 2 - Caso tenho o registro é retornado
// 3 - Caso não tenha registro no cache, a consulta é executado no banco de dados
// 4 - O resultado da consulta é salvo no cache
// 5 - O resultado da consulta é retornado
return sqlDB.NewCachedQuery(ctx, cache,
"SELECT * FROM usuarios WHERE id = $1", 1).One()
}
2. Cache de Lista
func ListarProdutos(ctx context.Context) ([]Produto, error) {
cache := NewCache[Produto]("produtos", 15 * time.Minute)
// Tentar recuperar lista do cache
produtos, err := cache.Many(ctx)
if err == nil && len(produtos) > 0 {
return produtos, nil
}
// Recuperar lista de produtos de outra camada da aplicação
produtos, err = produtos.ListarProdutos()
if err != nil {
return nil, err
}
// Atualizar cache
if err := cache.Set(ctx, produtos); err != nil {
log.Warn("falha ao atualizar cache", err)
}
return produtos, nil
}
3. Invalidação de Cache
func AtualizarProduto(ctx context.Context, produto Produto) error {
// Atualizar no banco
if err := db.AtualizarProduto(produto); err != nil {
return err
}
// Invalidar cache
cache := NewCache[Produto]("produtos", time.Hour)
if err := cache.Del(ctx); err != nil {
log.Warn("falha ao invalidar cache", err)
}
return nil
}
4. Cache com Prefixo de Aplicação
O cache automaticamente utiliza o nome da aplicação como prefixo:
cache := NewCache[Usuario]("usuarios", time.Hour)
// Internamente armazena como "MinhaApp::usuarios"
// `MinhaApp` é o valor definido na variável de ambiente 'APP_NAME'
Este design permite:
- Isolamento entre diferentes aplicações
- Evita conflitos de nomenclatura
- Facilita a depuração
Boas Práticas
-
Defina TTLs apropriados:
- Dados frequentemente atualizados: TTL curto
- Dados estáticos: TTL longo
- Evite TTL zero (sem expiração)
-
Trate falhas graciosamente:
- Sempre tenha um fallback quando o cache falhar
- Não bloqueie operações críticas por falhas no cache
-
Monitore o uso:
- Configure alertas para falhas de conexão
- Monitore o hit/miss ratio
- Observe o uso de memória
-
Mantenha a consistência:
- Invalide caches relacionados juntos
- Use nomes de cache consistentes
- Documente os TTLs utilizados