github.com/benz9527/xboot@v0.0.0-20240504061247-c23f15593274/timer/timing_wheel_intf.go (about)

     1  package timer
     2  
     3  // Ref:
     4  // https://dl.acm.org/doi/10.1145/41457.37504
     5  // https://ieeexplore.ieee.org/document/650142
     6  // github ref:
     7  // https://github.com/apache/kafka/tree/trunk/server-common/src/main/java/org/apache/kafka/server/util/timer
     8  // https://github.com/RussellLuo/timingwheel (Not a good implementation)
     9  
    10  import (
    11  	"context"
    12  	"time"
    13  )
    14  
    15  type twError string
    16  
    17  func (e twError) Error() string { return string(e) }
    18  
    19  const (
    20  	ErrTimingWheelStopped                   = twError("[timing-wheels] stopped")
    21  	ErrTimingWheelTaskNotFound              = twError("[timing-wheels] task not found")
    22  	ErrTimingWheelTaskEmptyJobID            = twError("[timing-wheels] empty job id in task")
    23  	ErrTimingWheelEmptyJob                  = twError("[timing-wheels] empty job in task")
    24  	ErrTimingWheelTaskIsExpired             = twError("[timing-wheels] task is expired")
    25  	ErrTimingWheelTaskUnableToBeAddedToSlot = twError("[timing-wheels] task unable to be added to a flushed slot")
    26  	ErrTimingWheelTaskUnableToBeRemoved     = twError("[timing-wheels] task unable to be removed")
    27  	ErrTimingWheelTaskTooShortExpiration    = twError("[timing-wheels] task expiration is too short")
    28  	ErrTimingWheelUnknownScheduler          = twError("[timing-wheels] unknown schedule")
    29  	ErrTimingWheelTaskCancelled             = twError("[timing-wheels] task cancelled")
    30  )
    31  
    32  type TimingWheelCommonMetadata interface {
    33  	// GetTickMs returns the baseline tick ms (interval) of the timing-wheel.
    34  	GetTickMs() int64
    35  	// GetStartMs returns the start ms of the timing-wheel.
    36  	GetStartMs() int64
    37  }
    38  
    39  type TimingWheel interface {
    40  	TimingWheelCommonMetadata
    41  	GetInterval() int64
    42  	GetCurrentTimeMs() int64
    43  }
    44  
    45  type Scheduler interface {
    46  	// next returns the next expiredMs.
    47  	next(beginMs int64) (nextExpiredMs int64)
    48  	// GetRestLoopCount returns the rest loop count.
    49  	// If the rest loop count is -1, it means that the task will run forever unless cancel manually.
    50  	GetRestLoopCount() int64
    51  }
    52  
    53  type TimingWheels interface {
    54  	TimingWheelCommonMetadata
    55  	// AddTask adds a task to the timing wheels.
    56  	AddTask(task Task) error
    57  	// CancelTask cancels a task by jobID.
    58  	CancelTask(jobID JobID) error
    59  	// Shutdown stops the timing wheels
    60  	Shutdown()
    61  	// AfterFunc schedules a function to run after the duration delayMs.
    62  	AfterFunc(delayMs time.Duration, fn Job) (Task, error)
    63  	// ScheduleFunc schedules a function to run at a certain time generated by the schedule.
    64  	ScheduleFunc(schedFn func() Scheduler, fn Job) (Task, error)
    65  }
    66  
    67  // JobID is the unique identifier of a job
    68  type JobID string
    69  
    70  // Job is the function that will be executed by the timing wheel
    71  type Job func(ctx context.Context, metadata JobMetadata)
    72  
    73  type JobType uint8
    74  
    75  const (
    76  	OnceJob JobType = iota
    77  	RepeatedJob
    78  )
    79  
    80  func (t JobType) String() string {
    81  	switch t {
    82  	case OnceJob:
    83  		return "once"
    84  	case RepeatedJob:
    85  		return "repeat"
    86  	default:
    87  	}
    88  	return "unknown"
    89  }
    90  
    91  // All metadata interfaces are designed for debugging and monitoring friendly.
    92  
    93  // JobMetadata describes the metadata of a job
    94  // Each slot in the timing wheel is a linked list of jobs
    95  type JobMetadata interface {
    96  	// GetJobID returns the jobID of the job, unique identifier.
    97  	GetJobID() JobID
    98  	// GetExpiredMs returns the expirationMs of the job.
    99  	GetExpiredMs() int64
   100  	// GetRestLoopCount returns the rest loop count.
   101  	GetRestLoopCount() int64
   102  	// GetJobType returns the job type.
   103  	GetJobType() JobType
   104  }
   105  
   106  // Task is the interface that wraps the Job
   107  type Task interface {
   108  	JobMetadata
   109  	GetJobMetadata() JobMetadata
   110  	// GetJob returns the job function.
   111  	GetJob() Job
   112  	// GetSlot returns the slot of the job.
   113  	GetSlot() TimingWheelSlot
   114  	// setSlot sets the slot of the job, it is a private method.
   115  	setSlot(slot TimingWheelSlot)
   116  	// GetPreviousSlotMetadata returns the previous slot metadata of the job.
   117  	GetPreviousSlotMetadata() TimingWheelSlotMetadata
   118  	// setPreviousSlotMetadata sets the current slot metadata of the job.
   119  	setSlotMetadata(slotMetadata TimingWheelSlotMetadata)
   120  	Cancel() bool
   121  	Cancelled() bool
   122  }
   123  
   124  // ScheduledTask is the interface that wraps the repeat Job
   125  type ScheduledTask interface {
   126  	Task
   127  	UpdateNextScheduledMs()
   128  }
   129  
   130  // TaskHandler is a function that reinserts a task into the timing wheel.
   131  // It means that the task should be executed periodically or repeatedly for a certain times.
   132  // Reinsert will add current task to next slot, higher level slot (overflow wheel) or
   133  // the same level slot (current wheel) depending on the expirationMs of the task.
   134  // When the task is reinserted, the expirationMs of the task should be updated.
   135  //  1. Check if the task is cancelled. If so, stop reinserting.
   136  //  2. Check if the task's loop count is greater than 0. If so, decrease the loop count and reinsert.
   137  //  3. Check if the task's loop count is -1 (run forever unless cancel manually).
   138  //     If so, reinsert and update the expirationMs.
   139  type TaskHandler func(Task) // Core function
   140  
   141  type TimingWheelSlotMetadata interface {
   142  	// GetExpirationMs returns the expirationMs of the slot.
   143  	GetExpirationMs() int64
   144  	// setExpirationMs sets the expirationMs of the slot.
   145  	setExpirationMs(expirationMs int64) bool
   146  	// GetSlotID returns the slotID of the slot, easy for debugging.
   147  	GetSlotID() int64
   148  	// setSlotID sets the slotID of the slot, easy for debugging.
   149  	setSlotID(slotID int64)
   150  	// GetLevel returns the level of the slot, easy for debugging.
   151  	GetLevel() int64
   152  	// setLevel sets the level of the slot, easy for debugging.
   153  	setLevel(level int64)
   154  }
   155  
   156  // TimingWheelSlot is the interface that wraps the slot, in kafka, it is called bucket.
   157  type TimingWheelSlot interface {
   158  	TimingWheelSlotMetadata
   159  	// GetMetadata returns the metadata of the slot.
   160  	GetMetadata() TimingWheelSlotMetadata
   161  	// AddTask adds a task to the slot.
   162  	AddTask(Task) error
   163  	// RemoveTask removes a task from the slot.
   164  	RemoveTask(Task) bool
   165  	// Flush flushes all tasks in the slot generally,
   166  	// but it should be called in a loop.
   167  	Flush(TaskHandler)
   168  }