| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950 |
- // Package store wraps Redis and Postgres. M0 ships Redis only; the
- // Postgres layer lands with M2 (recipient resolution).
- //
- // Redis is used for:
- // - dedupe (SET NX EX, INCR) – SPEC §5
- // - rate limiting (INCR + EXPIRE, or redis-cell) – SPEC §22 layer 3/4
- // - per-source quarantine (local map, see ingestd) – SPEC §22 layer 7
- package store
- import (
- "context"
- "fmt"
- "time"
- "github.com/redis/go-redis/v9"
- )
- // Redis is a thin wrapper to keep construction in one place.
- type Redis struct {
- *redis.Client
- }
- // ConnectRedis dials Redis. Two retries with backoff.
- func ConnectRedis(ctx context.Context, url string) (*Redis, error) {
- opts, err := redis.ParseURL(url)
- if err != nil {
- return nil, fmt.Errorf("parse redis url %q: %w", url, err)
- }
- c := redis.NewClient(opts)
- pingCtx, cancel := context.WithTimeout(ctx, 3*time.Second)
- defer cancel()
- if err := c.Ping(pingCtx).Err(); err != nil {
- return nil, fmt.Errorf("redis ping: %w", err)
- }
- return &Redis{c}, nil
- }
- // IsConnRefused reports whether err looks like Redis being down.
- func IsConnRefused(err error) bool {
- return err != nil && (contains(err.Error(), "connection refused") || contains(err.Error(), "EOF"))
- }
- func contains(s, sub string) bool {
- for i := 0; i+len(sub) <= len(s); i++ {
- if s[i:i+len(sub)] == sub {
- return true
- }
- }
- return false
- }
|