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  }