github.com/lingyao2333/mo-zero@v1.4.1/core/stores/cache/cleaner.go (about) 1 package cache 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/lingyao2333/mo-zero/core/collection" 8 "github.com/lingyao2333/mo-zero/core/logx" 9 "github.com/lingyao2333/mo-zero/core/proc" 10 "github.com/lingyao2333/mo-zero/core/stat" 11 "github.com/lingyao2333/mo-zero/core/stringx" 12 "github.com/lingyao2333/mo-zero/core/threading" 13 ) 14 15 const ( 16 timingWheelSlots = 300 17 cleanWorkers = 5 18 taskKeyLen = 8 19 ) 20 21 var ( 22 timingWheel *collection.TimingWheel 23 taskRunner = threading.NewTaskRunner(cleanWorkers) 24 ) 25 26 type delayTask struct { 27 delay time.Duration 28 task func() error 29 keys []string 30 } 31 32 func init() { 33 var err error 34 timingWheel, err = collection.NewTimingWheel(time.Second, timingWheelSlots, clean) 35 logx.Must(err) 36 37 proc.AddShutdownListener(func() { 38 timingWheel.Drain(clean) 39 }) 40 } 41 42 // AddCleanTask adds a clean task on given keys. 43 func AddCleanTask(task func() error, keys ...string) { 44 timingWheel.SetTimer(stringx.Randn(taskKeyLen), delayTask{ 45 delay: time.Second, 46 task: task, 47 keys: keys, 48 }, time.Second) 49 } 50 51 func clean(key, value interface{}) { 52 taskRunner.Schedule(func() { 53 dt := value.(delayTask) 54 err := dt.task() 55 if err == nil { 56 return 57 } 58 59 next, ok := nextDelay(dt.delay) 60 if ok { 61 dt.delay = next 62 timingWheel.SetTimer(key, dt, next) 63 } else { 64 msg := fmt.Sprintf("retried but failed to clear cache with keys: %q, error: %v", 65 formatKeys(dt.keys), err) 66 logx.Error(msg) 67 stat.Report(msg) 68 } 69 }) 70 } 71 72 func nextDelay(delay time.Duration) (time.Duration, bool) { 73 switch delay { 74 case time.Second: 75 return time.Second * 5, true 76 case time.Second * 5: 77 return time.Minute, true 78 case time.Minute: 79 return time.Minute * 5, true 80 case time.Minute * 5: 81 return time.Hour, true 82 default: 83 return 0, false 84 } 85 }