package subscriber import ( "context" "crypto/sha256" "encoding/hex" "sync" "time" "aiaa-notification-service/internal/cache" ) type Deduper interface { Claim(ctx context.Context, hash string) (bool, error) Release(ctx context.Context, hash string) error } func MessageHash(body []byte) string { sum := sha256.Sum256(body) return hex.EncodeToString(sum[:]) } type MemoryDeduper struct { mu sync.Mutex seen map[string]struct{} } func NewMemoryDeduper() *MemoryDeduper { return &MemoryDeduper{seen: make(map[string]struct{})} } func (d *MemoryDeduper) Claim(_ context.Context, hash string) (bool, error) { d.mu.Lock() defer d.mu.Unlock() if _, ok := d.seen[hash]; ok { return false, nil } d.seen[hash] = struct{}{} return true, nil } func (d *MemoryDeduper) Release(_ context.Context, hash string) error { d.mu.Lock() defer d.mu.Unlock() delete(d.seen, hash) return nil } type cacheDeduper struct { c *cache.Cache ttl time.Duration } func NewCacheDeduper(c *cache.Cache, ttl time.Duration) Deduper { if c == nil { return NewMemoryDeduper() } if ttl <= 0 { ttl = time.Hour } return &cacheDeduper{c: c, ttl: ttl} } func (d *cacheDeduper) Claim(ctx context.Context, hash string) (bool, error) { return d.c.ClaimDedup(ctx, hash, d.ttl) } func (d *cacheDeduper) Release(ctx context.Context, hash string) error { return d.c.ReleaseDedup(ctx, hash) }