init push
This commit is contained in:
95
internal/queue/idempotency.go
Normal file
95
internal/queue/idempotency.go
Normal file
@@ -0,0 +1,95 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user