bf9395e022
CI / license-header (push) Has been skipped
CI / e2e-dry-run (push) Has been skipped
CI / fast-gate (push) Failing after 0s
Test PR Label Logic / test-pr-labels (push) Failing after 1s
Skill Format Check / check-format (push) Failing after 2s
CI / security (push) Failing after 5s
CI / unit-test (push) Has been skipped
CI / lint (push) Has been skipped
CI / script-test (push) Has been skipped
CI / deterministic-gate (push) Has been skipped
CI / coverage (push) Has been skipped
CI / results (push) Has been cancelled
CI / deadcode (push) Has been cancelled
CI / e2e-live (push) Has been cancelled
72 lines
1.3 KiB
Go
72 lines
1.3 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package event
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
defaultDedupTTL = 5 * time.Minute
|
|
defaultRingSize = 10000
|
|
)
|
|
|
|
// DedupFilter: seen map is sole authority; ring only bounds map size via overflow eviction.
|
|
type DedupFilter struct {
|
|
seen map[string]time.Time
|
|
ring []string
|
|
pos int
|
|
ttl time.Duration
|
|
mu sync.Mutex
|
|
}
|
|
|
|
func NewDedupFilter() *DedupFilter {
|
|
return NewDedupFilterWithSize(defaultRingSize, defaultDedupTTL)
|
|
}
|
|
|
|
func NewDedupFilterWithSize(ringSize int, ttl time.Duration) *DedupFilter {
|
|
return &DedupFilter{
|
|
seen: make(map[string]time.Time),
|
|
ring: make([]string, ringSize),
|
|
ttl: ttl,
|
|
}
|
|
}
|
|
|
|
func (d *DedupFilter) IsDuplicate(eventID string) bool {
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
|
|
now := time.Now()
|
|
|
|
if ts, ok := d.seen[eventID]; ok {
|
|
if now.Sub(ts) < d.ttl {
|
|
return true
|
|
}
|
|
delete(d.seen, eventID)
|
|
}
|
|
|
|
d.seen[eventID] = now
|
|
|
|
if old := d.ring[d.pos]; old != "" && old != eventID {
|
|
delete(d.seen, old)
|
|
}
|
|
d.ring[d.pos] = eventID
|
|
d.pos = (d.pos + 1) % len(d.ring)
|
|
|
|
if d.pos%1000 == 0 {
|
|
d.cleanupExpired(now)
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (d *DedupFilter) cleanupExpired(now time.Time) {
|
|
for id, ts := range d.seen {
|
|
if now.Sub(ts) >= d.ttl {
|
|
delete(d.seen, id)
|
|
}
|
|
}
|
|
}
|