I have a HTTP handler which receives a parameter from query. I don't want to run this handler concurrently for the same query parameter i.e. only one goroutine should be running at a point in time.
This was my idea:
import "sync"
import "fmt"
var safeMap = sync.Map{}
func handler(c) {
_, loaded := safeMap.LoadOrStore(c.param, 1) // loaded is true if value was loaded and false if stored
fmt.Println(loaded)
if loaded {
c.JSON(http.StatusLocked, "locked")
return
}
go doWork(c.param)
safeMap.Delete(c.param)
c.JSON(http.StatusOK, "done")
}
But, whenever I send two concurrent requests loaded
is false for both and both requests are processed. Why?
You're starting a goroutine to do the work, then immediately deleting the key, before the work has a chance to complete. So the chance of lock contention is infinitesimally low.
Why are you using go doWork()
instead of just doWork()
?
The problem is that you do your work (doWork()
) in a new goroutine. If param is not yet locked, the handler locks it, and launches a goroutine to do the work. But the goroutine serving the request does not wait for the work to complete, it unlocks the param immediately.
Instead do the work in the handler, so unlocking the param will only happen once doWork()
returns:
doWork(c.param) // Note no 'go' keyword