In answering another question I wrote a little struct using sync.Map
to cache API requests.
type PostManager struct {
sync.Map
}
func (pc PostManager) Fetch(id int) Post {
post, ok := pc.Load(id)
if ok {
fmt.Printf("Using cached post %v\n", id)
return post.(Post)
}
fmt.Printf("Fetching post %v\n", id)
post = pc.fetchPost(id)
pc.Store(id, post)
return post.(Post)
}
Unfortunately, if two goroutines both fetch the same uncached Post at the same time, both will make a request.
var postManager PostManager
wg.Add(3)
var firstPost Post
var secondPost Post
var secondPostAgain Post
go func() {
// Fetches and caches 1
firstPost = postManager.Fetch(1)
defer wg.Done()
}()
go func() {
// Fetches and caches 2
secondPost = postManager.Fetch(2)
defer wg.Done()
}()
go func() {
// Also fetches and caches 2
secondPostAgain = postManager.Fetch(2)
defer wg.Done()
}()
wg.Wait()
I need to ensure when there are simultaneous fetches of the same ID only one is allowed to actually make the request. The other must wait and will use the cached Post. But to also not lock out fetches of different IDs.
In the above example, I want there to be one and only one call to pc.fetchPost(1)
and pc.fetchPost(2)
and they should be simultaneous.