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