Resetting a ticker in Go

Arpith Siromoney
2 min readJul 14, 2016

--

Raft is the distributed consensus algorithm that I’m using for the database I’m building at the Recurse Center. The algorithm has two timeouts — a heartbeat timeout and an election timeout. When a server receives a heartbeat from the leader, it resets its election timeout. If it doesn’t receive a heartbeat before its election timeout runs out, it tries to elect itself as leader!

As a first step towards this, I need to write code that resets a timeout (say 2 seconds) every time another timeout (say 5 seconds) expires. I would expect to see output that looked like:

Elapsed: 0
Elapsed: 2 Ticker A
Elapsed: 4 Ticker A
Elapsed: 5 Ticker B - Going to reset Ticker A!
Elapsed: 7 Ticker A
Elapsed: 9 Ticker A

If I hadn’t reset the other ticker, of course, I would expect to see:

Elapsed: 0 
Elapsed: 2 Ticker A
Elapsed: 4 Ticker A
Elapsed: 5 Ticker B
Elapsed: 6 Ticker A
Elapsed: 8 Ticker A

Here’s how I wrote this in Go, using ticking channels. I set up a goroutine that listens on two channels, tickerA and tickerB. Every time tickerB goes off, it resets tickerA.

type server struct {
doneChan chan bool
tickerA ticker
tickerB ticker
}
func (s *server) listener() {
start := time.Now()
for {
select {
case <-s.tickerA.ticker.C:
elapsed := time.Since(start)
fmt.Println("Elapsed: ", elapsed, " Ticker A")
case <-s.tickerB.ticker.C:
s.tickerA.resetTicker()
elapsed := time.Since(start)
fmt.Println("Elapsed: ", elapsed, " Ticker B - Going to reset ticker A")
}
}
s.doneChan <- true
}
func main() {
doneChan := make(chan bool)
tickerA := createTicker(2 * time.Second)
tickerB := createTicker(5 * time.Second)
s := &server{doneChan, *tickerA, *tickerB}
go s.listener()
<-doneChan
}

And my tickers and the methods to create and reset them look like:

type ticker struct {
period time.Duration
ticker time.Ticker
}
func createTicker(period time.Duration) *ticker {
return &ticker{period, *time.NewTicker(period)}
}
func (t *ticker) resetTicker() {
t.ticker = *time.NewTicker(t.period)
}

The actual output is:

$ go run tick.goElapsed:  2.00074535s  Ticker AElapsed:  4.002086857s  Ticker AElapsed:  5.004973744s  Ticker B - Going to reset ticker AElapsed:  7.008515813s  Ticker AElapsed:  9.009572001s  Ticker A

That’s about it! Check it out!

Have a better way to do this? Just want to say hi? I’m on Twitter!

--

--