// Package rediscache implements the LinkCache port on Redis. This is the layer // that keeps the redirect hot path off Postgres. package rediscache import ( "context" "encoding/json" "time" "github.com/redis/go-redis/v9" "github.com/snip/backend/internal/port" ) type Cache struct { rdb *redis.Client prefix string } func New(ctx context.Context, addr, password string, db int) (*Cache, error) { rdb := redis.NewClient(&redis.Options{Addr: addr, Password: password, DB: db}) if err := rdb.Ping(ctx).Err(); err != nil { _ = rdb.Close() return nil, err } return &Cache{rdb: rdb, prefix: "code:"}, nil } func (c *Cache) key(code string) string { return c.prefix + code } func (c *Cache) GetResolution(ctx context.Context, code string) (port.Resolution, bool, error) { b, err := c.rdb.Get(ctx, c.key(code)).Bytes() if err == redis.Nil { return port.Resolution{}, false, nil } if err != nil { return port.Resolution{}, false, err } var r port.Resolution if err := json.Unmarshal(b, &r); err != nil { return port.Resolution{}, false, err } return r, true, nil } func (c *Cache) SetResolution(ctx context.Context, code string, r port.Resolution, ttl time.Duration) error { b, err := json.Marshal(r) if err != nil { return err } return c.rdb.Set(ctx, c.key(code), b, ttl).Err() } func (c *Cache) Invalidate(ctx context.Context, code string) error { return c.rdb.Del(ctx, c.key(code)).Err() } func (c *Cache) Close() error { return c.rdb.Close() }