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 }