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