Skip to content

Commit 2a83544

Browse files
cdaguerredarkweak
andauthored
feat: avoid multiple simult. evictions with simple lock (#701)
* feat: avoid multiple simult. evictions with simple lock * fix(test): set lock key wihtout mapping prefix --------- Co-authored-by: darkweak <darkweak@protonmail.com>
1 parent 0b778c7 commit 2a83544

File tree

3 files changed

+83
-14
lines changed

3 files changed

+83
-14
lines changed

pkg/middleware/middleware.go

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,60 @@ func reorderStorers(storers []types.Storer, expectedStorers []string) []types.St
5050
return newStorers
5151
}
5252

53+
const (
54+
evictionLockKey = "eviction-lock"
55+
evictionLockTTL = 2 * time.Minute
56+
)
57+
58+
// evictionLockHolder is a unique identifier for this instance, used for distributed lock ownership.
59+
var evictionLockHolder = uuid.NewString()
60+
61+
func tryAcquireEvictionLock(storer types.Storer) bool {
62+
now := time.Now()
63+
existing := storer.Get(evictionLockKey)
64+
65+
if len(existing) > 0 {
66+
// Lock value format: "holder_id|expiry_timestamp"
67+
parts := strings.SplitN(string(existing), "|", 2)
68+
if len(parts) == 2 {
69+
holderID := parts[0]
70+
lockedUntil, err := time.Parse(time.RFC3339, parts[1])
71+
if err == nil && now.Before(lockedUntil) {
72+
// Lock is still valid - check if we own it
73+
if holderID == evictionLockHolder {
74+
return true
75+
}
76+
return false
77+
}
78+
}
79+
}
80+
81+
// Lock expired or doesn't exist - attempt to claim it using optimistic locking
82+
newLockExpiry := now.Add(evictionLockTTL)
83+
lockValue := evictionLockHolder + "|" + newLockExpiry.Format(time.RFC3339)
84+
if err := storer.Set(evictionLockKey, []byte(lockValue), evictionLockTTL); err != nil {
85+
return false
86+
}
87+
88+
// Verify we actually got the lock (optimistic locking)
89+
// Another instance might have written between our check and set
90+
time.Sleep(10 * time.Millisecond)
91+
verifyValue := storer.Get(evictionLockKey)
92+
return string(verifyValue) == lockValue
93+
}
94+
5395
func registerMappingKeysEviction(logger core.Logger, storers []types.Storer) {
5496
for _, storer := range storers {
5597
logger.Debugf("registering mapping eviction for storer %s", storer.Name())
5698
go func(current types.Storer) {
5799
for {
58-
logger.Debugf("run mapping eviction for storer %s", current.Name())
100+
if !tryAcquireEvictionLock(current) {
101+
logger.Debugf("skipping mapping eviction for storer %s, another instance holds the lock", current.Name())
102+
time.Sleep(time.Minute)
103+
continue
104+
}
59105

106+
logger.Debugf("run mapping eviction for storer %s", current.Name())
60107
api.EvictMapping(current)
61108
}
62109
}(storer)

plugins/traefik/vendor/github.com/darkweak/souin/pkg/surrogate/providers/common.go

Lines changed: 34 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

plugins/traefik/vendor/github.com/darkweak/souin/pkg/surrogate/providers/types.go

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)