Tuesday, April 13, 2021

Throttle API calls in GO


 


In this post we will review how to throttle API calls to a library in GO.

We assume that the library does some updates upon a notification. In this case, the notification does not contain any data, it simply means: "Something changed, do your stuff". 

Now, we want the update to occur no more than once in a minute. It does not matter how many time it was notified, only that the library does its work no more than once in a minute.

To do this, we use an updates channel, and a GO routine. The throttle struct includes the channel for updates, as well as the configuration of throttle.



package throttle

import (
"time"
)

type Handler func() error

type Throttle struct {
interval time.Duration
handler Handler
updates chan bool
lastRun time.Time
}

func CreateThrottle(
interval time.Duration,
handler Handler,
) *Throttle {
return &Throttle{
interval: interval,
handler: handler,
updates: make(chan bool, 100),
lastRun: time.Now(),
}
}




Next we include the API calls for the throttle:



func (t *Throttle) Start() {
go t.channelLoop()
}

func (t *Throttle) Notify() {
t.updates <- true
}



and last, we implement the GO routine to handle the updates. Notice that in case the notifications are too frequent, we schedule a later run of the API upon a timer.



func (t *Throttle) channelLoop() {
var nextTimeout *time.Duration

for {
if nextTimeout == nil {
_ = <-t.updates
} else {
select {
case _ = <-t.updates:
case <-time.After(*nextTimeout):
}
}

passedTime := time.Now().Sub(t.lastRun)
if passedTime < t.interval {
timeout := t.interval - passedTime
nextTimeout = &timeout
} else {
err := t.handler()
if err != nil {
panic(err)
}
t.lastRun = time.Now()
nextTimeout = nil
}
}
}



That's it! 

The throttle API is ready. Let's see and example of usage:



throttling = CreateThrottle(time.Minute, myExecutor)
throttling.Start()












No comments:

Post a Comment