Skip to content

Commit 2d33d69

Browse files
committed
fix(expiry): randomise key sampling to eliminate sync.Map iteration bias
Signed-off-by: Ankush Chavan <cankush625@gmail.com>
1 parent 9f368fb commit 2d33d69

1 file changed

Lines changed: 24 additions & 6 deletions

File tree

db/expiry.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package db
22

33
import (
4+
"math/rand"
45
"time"
56
)
67

@@ -47,18 +48,35 @@ func StartActiveExpiry(stop <-chan struct{}) {
4748
}()
4849
}
4950

50-
// cleanExpiredKeys scans up to chunkSize keys from KeyTTL, deletes any that
51-
// have expired, and returns the number of keys scanned and the number deleted.
51+
// cleanExpiredKeys samples up to chunkSize keys from KeyTTL in random order,
52+
// deletes any that have expired, and returns the number of keys sampled and
53+
// the number deleted.
54+
// Randomisation ensures that all keys get a fair chance of being checked
55+
// regardless of insertion order, avoiding the bias that sync.Map.Range's
56+
// consistent iteration order would otherwise introduce.
5257
func cleanExpiredKeys() (total int, expired int) {
58+
// Collect all keys that have a TTL set.
59+
var keys []any
60+
KeyTTL.Range(func(key, _ any) bool {
61+
keys = append(keys, key)
62+
return true
63+
})
64+
65+
// Shuffle so every key has an equal chance of landing in the sample window.
66+
rand.Shuffle(len(keys), func(i, j int) { keys[i], keys[j] = keys[j], keys[i] })
67+
5368
now := time.Now().UnixMilli()
54-
KeyTTL.Range(func(key, value any) bool {
69+
for _, key := range keys {
70+
if total >= chunkSize {
71+
break
72+
}
5573
total++
56-
if value != nil && value.(int64) < now {
74+
value, ok := KeyTTL.Load(key)
75+
if ok && value != nil && value.(int64) < now {
5776
KeyTTL.Delete(key)
5877
DB.Delete(key)
5978
expired++
6079
}
61-
return total < chunkSize
62-
})
80+
}
6381
return
6482
}

0 commit comments

Comments
 (0)