go-zero icon indicating copy to clipboard operation
go-zero copied to clipboard

How does go-zero avoid pseudo-random selection in timingwheel?

Open EyTest-bang opened this issue 1 year ago • 2 comments

func (tw *TimingWheel) run() {
	for {
		select {
		case <-tw.ticker.Chan():
			tw.onTick()
		case task := <-tw.setChannel:
			tw.setTask(&task)
		case key := <-tw.removeChannel:
			tw.removeTask(key)
		case task := <-tw.moveChannel:
			tw.moveTask(task)
		case fn := <-tw.drainChannel:
			tw.drainAll(fn)
		case <-tw.stopChannel:
			tw.ticker.Stop()
			return
		}
	}
}

If more than one channel can proceed, won't code above conflict? Note the following scenarios:

  1. If user set small tick like 100ms or smaller at beginning for each slot, and user set a new task when tick on, if setChannel is selected, it's ok although current slot's tasks will be delayed for 100ms or less, but what if ticker.Chan is selected, new task disappears.
  2. It's also a big problem that if stopChannel is ignored.
  3. Futhermore, in extreme cases, a large duration suck as one minute like _test.go, some tasks will be delayed for so much time

I try to change this function like below:

func (tw *TimingWheel) run() {
	for {
		select {
		case <-tw.stopChannel:
			tw.ticker.Stop()
			return
		default:
		}
		select {
		case <-tw.ticker.Chan():
			tw.onTick()
		default:
		}
		select {
		case task := <-tw.setChannel:
			tw.setTask(&task)
		case key := <-tw.removeChannel:
			tw.removeTask(key)
		case task := <-tw.moveChannel:
			tw.moveTask(task)
		case fn := <-tw.drainChannel:
			tw.drainAll(fn)
		default:
		}
	}
}

It doesn't work well if test entire file, but it's passed all tests seperatelly. Are there some shared variables? Or I made a mistake.

Hope for you reply.

Reference: https://stackoverflow.com/questions/47645808/how-does-select-work-when-multiple-channels-are-involved https://stackoverflow.com/questions/46200343/force-priority-of-go-select-statement/46202533#46202533

EyTest-bang avatar May 26 '23 12:05 EyTest-bang