Skip to content

Instantly share code, notes, and snippets.

@kayslay
Created May 27, 2020 06:33
Show Gist options
  • Save kayslay/ae7c3d74de74244b3a6ca66c69730a73 to your computer and use it in GitHub Desktop.
Save kayslay/ae7c3d74de74244b3a6ca66c69730a73 to your computer and use it in GitHub Desktop.
package main
import (
"log"
"sync"
"time"
)
var (
duration = time.Second * 5
)
type infoModel struct {
// INFO FIELDS
Name string
Age int
Sex string
Hobbies []string
InfoID string
}
// infoSaver defines the interface required to save infoModel to a db
type infoSaver interface {
CreateInfo(i *infoModel) error
}
// infoChan the struct passed in the channel
// contains the info model and an error
type infoChan struct {
info infoModel
err error
}
// infoBatchRequester this handles the request to the slow api/operation
// ensures only the first request call the api/op
type infoBatchRequester struct {
mx sync.RWMutex
pending map[string][]chan<- infoChan // this holds pending requests
infoSaver
}
// Request makes request to the api
func (i *infoBatchRequester) Request(infoID string, result chan<- infoChan) {
var done chan infoChan
i.mx.Lock()
_, ok := i.pending[infoID]
i.mx.Unlock()
if !ok {
// create the first pending request
done = make(chan infoChan)
i.mx.Lock()
i.pending[infoID] = []chan<- infoChan{result}
i.mx.Unlock()
// make the request to get the info
go func() {
var (
infoResp infoModel
err error
)
//pretend to do long running task
log.Println("DOING LONG RUNNING TASK FOR. CALLED ONCE", infoID)
time.Sleep(duration)
infoResp = infoModel{
Name: "John Doe",
Age: 50,
Sex: "Male",
Hobbies: []string{"eating"},
InfoID: infoID,
}
//send info and error to done chan
done <- infoChan{info: infoResp, err: err}
}()
} else {
// append info to list of pending requests with the same infoID
i.mx.Lock()
i.pending[infoID] = append(i.pending[infoID], result)
i.mx.Unlock()
return
}
//wait for done to receive a value
value := <-done
i.mx.RLock()
results, ok := i.pending[infoID]
i.mx.RUnlock()
if ok {
for _, r := range results {
r <- value
close(r)
}
}
defer func() {
//delete the key when func execution is complete
i.mx.Lock()
delete(i.pending, infoID)
i.mx.Unlock()
}()
if value.err == nil {
log.Println("SAVED TO DB")
log.Println(i.infoSaver.CreateInfo(&value.info))
} else {
log.Println("Error returned", value.err)
}
return
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment