init ingestion
This commit is contained in:
50
ingestion/ingest/internal/dedup/dedup.go
Normal file
50
ingestion/ingest/internal/dedup/dedup.go
Normal file
@@ -0,0 +1,50 @@
|
||||
// Package dedup provides idempotent event acceptance via Redis SETNX.
|
||||
//
|
||||
// Key shape: dedup:{workspace_id}:{message_id}
|
||||
// TTL: 24h by default (configurable)
|
||||
//
|
||||
// CheckAndSet returns true when the message_id is new (first time seen).
|
||||
// If it returns false the caller MUST drop the event silently and return 200.
|
||||
package dedup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/redis/rueidis"
|
||||
)
|
||||
|
||||
type Dedup interface {
|
||||
CheckAndSet(ctx context.Context, workspaceID, messageID string) (bool, error)
|
||||
}
|
||||
|
||||
type redisDedup struct {
|
||||
client rueidis.Client
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
func New(client rueidis.Client, ttl time.Duration) Dedup {
|
||||
return &redisDedup{client: client, ttl: ttl}
|
||||
}
|
||||
|
||||
func key(workspaceID, messageID string) string {
|
||||
return fmt.Sprintf("dedup:%s:%s", workspaceID, messageID)
|
||||
}
|
||||
|
||||
func (d *redisDedup) CheckAndSet(ctx context.Context, workspaceID, messageID string) (bool, error) {
|
||||
k := key(workspaceID, messageID)
|
||||
cmd := d.client.B().Set().Key(k).Value("1").
|
||||
Nx().
|
||||
Ex(d.ttl).
|
||||
Build()
|
||||
resp := d.client.Do(ctx, cmd)
|
||||
if err := resp.Error(); err != nil {
|
||||
return false, fmt.Errorf("dedup setnx: %w", err)
|
||||
}
|
||||
// SET with NX returns "OK" when set, nil reply when key already exists.
|
||||
if resp.IsNil() {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
Reference in New Issue
Block a user