Skip to content

Instantly share code, notes, and snippets.

@crossgate10
Last active December 4, 2023 09:39
Show Gist options
  • Save crossgate10/d14c85abd4348c346507f4a72b98ecc3 to your computer and use it in GitHub Desktop.
Save crossgate10/d14c85abd4348c346507f4a72b98ecc3 to your computer and use it in GitHub Desktop.
use goroutine to check all TTLs of redis keys
package main
import (
"context"
"sync"
"time"
"github.com/redis/go-redis/v9"
"github.com/rs/zerolog/log"
_ "net/http/pprof"
)
func timer(name string) func() {
start := time.Now()
return func() {
log.Info().Msgf("%s took %v", name, time.Since(start))
}
}
//func hang() {
// select {}
//}
func main() {
defer timer("total")()
// to sample for pprof
//defer hang()
//go func() {
// log.Info().Msgf("%v", http.ListenAndServe(":6060", nil))
//}()
ctx := context.Background()
c := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "YOUR_PWD",
DB: 0,
PoolSize: 300,
})
keys := getAllKeys(ctx, c)
totalKeys := len(keys)
// use goroutine to get ttl of each key by chunks
ch := make(chan map[string]time.Duration)
var wg sync.WaitGroup
chunkSize := 3000
for i := 0; i < totalKeys; i += chunkSize {
wg.Add(1)
go func(i int) {
defer wg.Done()
start, last := i, i+chunkSize
if last > totalKeys {
last = totalKeys
}
res := getTTLs(ctx, c, keys[start:last])
ch <- res
}(i)
}
// handle statistics
wg.Add(1)
go func() {
defer wg.Done()
defer timer("get all ttl")()
var longestKey string
var longestTTL time.Duration = -1
top, bottom := float64(500000), float64(100000)
cntTop, cntBottom := 0, 0
total := 0
sum := float64(0)
for {
select {
case res := <-ch:
ttls := make([]float64, 0, len(res)+1)
for key, ttl := range res {
sec := ttl.Seconds()
sum += sec
if sec > top {
cntTop++
}
if sec < bottom {
cntBottom++
}
if ttl > longestTTL {
longestKey = key
longestTTL = ttl
}
ttls = append(ttls, sec)
}
total += len(ttls)
if total == totalKeys {
avg := sum / float64(total)
log.Info().Msgf("max ttl: %v (=> %v s), and it's key: %s", longestTTL, longestTTL.Seconds(), longestKey)
log.Info().Msgf("avg: %v s, count(ttl > %v): %v, count(ttl < %v): %v", avg, top, cntTop, bottom, cntBottom)
return
}
}
}
}()
wg.Wait()
}
func getAllKeys(ctx context.Context, c *redis.Client) []string {
defer timer("get all keys")()
keys, err := c.Keys(ctx, "*").Result()
if err != nil {
log.Error().Msgf("%v", err)
return []string{}
}
return keys
}
func getTTLs(ctx context.Context, c *redis.Client, keys []string) map[string]time.Duration {
pipe := c.Pipeline()
durCmds := make(map[string]*redis.DurationCmd)
for _, key := range keys {
cmd := pipe.TTL(ctx, key)
durCmds[key] = cmd
}
if _, err := pipe.Exec(ctx); err != nil {
log.Error().Msgf("%v", err)
}
res := make(map[string]time.Duration)
for key, cmd := range durCmds {
res[key] = cmd.Val()
}
return res
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment