Skip to content

Cache & Base de données (Server)

Redis est strictement un cache de lecture. Toute erreur Redis est observée et, selon l’opération, dégradée en cache miss ; elle ne doit pas rendre PostgreSQL incohérent. Les écritures PostgreSQL sont la source de vérité et invalident les clés après succès.

Donnée Clé TTL indicatif
Application berth:application:{uuid} 60 s
Liste des applications berth:applications:list 15 s
Agent berth:agent:{uuid} 30 s
Session/révocation JWT berth:auth:revoked:{jti} durée restante du JWT

Le préfixe peut inclure un identifiant d’installation si plusieurs services partagent Redis. Les listes sont invalidées avec les entités qu’elles contiennent.

Implémentation complète du cache application

Section titled “Implémentation complète du cache application”
internal/modules/application/cache.go
package application
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/redis/go-redis/v9"
"github.com/google/uuid"
"github.com/your-org/berth-api/internal/domain"
)
type Cache interface {
GetApplication(context.Context, uuid.UUID) (*domain.Application, error)
SetApplication(context.Context, *domain.Application) error
InvalidateApplication(context.Context, uuid.UUID) error
}
type RedisCache struct { client *redis.Client; ttl time.Duration }
func NewRedisCache(client *redis.Client, ttl time.Duration) *RedisCache { return &RedisCache{client: client, ttl: ttl} }
func applicationKey(id uuid.UUID) string { return fmt.Sprintf("berth:application:%s", id) }
func (c *RedisCache) GetApplication(ctx context.Context, id uuid.UUID) (*domain.Application, error) {
raw, err := c.client.Get(ctx, applicationKey(id)).Bytes()
if err != nil { return nil, err }
var app domain.Application
if err := json.Unmarshal(raw, &app); err != nil { return nil, err }
return &app, nil
}
internal/domain/application.go
package domain
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
type Application struct {
ID uuid.UUID `gorm:"type:uuid;primaryKey" json:"id"`
Name string `gorm:"type:varchar(150);not null;index" json:"name"`
AgentID uuid.UUID `gorm:"type:uuid;not null;index" json:"agent_id"`
ContainerID string `gorm:"type:varchar(255);not null" json:"container_id"`
Status string `gorm:"type:varchar(32);not null;default:'unknown';index" json:"status"`
CreatedAt time.Time `gorm:"not null;autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"not null;autoUpdateTime" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}
func (a *Application) BeforeCreate(_ *gorm.DB) error {
if a.ID == uuid.Nil { a.ID = uuid.New() }
return nil
}
internal/modules/application/gorm_repository.go
package application
import (
"context"
"errors"
"github.com/google/uuid"
"gorm.io/gorm"
"github.com/your-org/berth-api/internal/domain"
"github.com/your-org/berth-api/internal/pkg/apperror"
)
type GORMRepository struct { db *gorm.DB }
func NewGORMRepository(db *gorm.DB) *GORMRepository { return &GORMRepository{db: db} }
func (r *GORMRepository) FindByID(ctx context.Context, id uuid.UUID) (*domain.Application, error) {
var app domain.Application
if err := r.db.WithContext(ctx).First(&app, "id = ?", id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { return nil, apperror.ErrNotFound }
return nil, err
}
return &app, nil
}