package queue import ( "context" "sync" "time" ) type IdempotencyState string const ( IdempotencyStateAcquired IdempotencyState = "acquired" IdempotencyStateDuplicate IdempotencyState = "duplicate" IdempotencyStateInProgress IdempotencyState = "in_progress" ) type IdempotencyStore interface { Acquire(ctx context.Context, key string, processingTTL time.Duration) (IdempotencyState, error) MarkCompleted(ctx context.Context, key string, ttl time.Duration) error Release(ctx context.Context, key string) error } type NoopIdempotencyStore struct{} func (NoopIdempotencyStore) Acquire(context.Context, string, time.Duration) (IdempotencyState, error) { return IdempotencyStateAcquired, nil } func (NoopIdempotencyStore) MarkCompleted(context.Context, string, time.Duration) error { return nil } func (NoopIdempotencyStore) Release(context.Context, string) error { return nil } type MemoryIdempotencyStore struct { mu sync.Mutex records map[string]memoryIdempotencyRecord now func() time.Time } type memoryIdempotencyRecord struct { state IdempotencyState expiresAt time.Time } func NewMemoryIdempotencyStore() *MemoryIdempotencyStore { return &MemoryIdempotencyStore{ records: make(map[string]memoryIdempotencyRecord), now: func() time.Time { return time.Now().UTC() }, } } func (s *MemoryIdempotencyStore) Acquire(_ context.Context, key string, processingTTL time.Duration) (IdempotencyState, error) { s.mu.Lock() defer s.mu.Unlock() s.cleanupExpiredLocked() if record, ok := s.records[key]; ok { return record.state, nil } s.records[key] = memoryIdempotencyRecord{ state: IdempotencyStateInProgress, expiresAt: s.now().Add(processingTTL), } return IdempotencyStateAcquired, nil } func (s *MemoryIdempotencyStore) MarkCompleted(_ context.Context, key string, ttl time.Duration) error { s.mu.Lock() defer s.mu.Unlock() s.records[key] = memoryIdempotencyRecord{ state: IdempotencyStateDuplicate, expiresAt: s.now().Add(ttl), } return nil } func (s *MemoryIdempotencyStore) Release(_ context.Context, key string) error { s.mu.Lock() defer s.mu.Unlock() delete(s.records, key) return nil } func (s *MemoryIdempotencyStore) cleanupExpiredLocked() { now := s.now() for key, record := range s.records { if !record.expiresAt.IsZero() && now.After(record.expiresAt) { delete(s.records, key) } } }