I am writing a golang api that accepts a tableName value and a updEpoch value, ie:
curl -F "tableName=abc" -F "updEpoch=123" myhost:8080/singleroute
curl -F "tableName=abc" -F "updEpoch=456" myhost:8080/singleroute
curl -F "tableName=def" -F "updEpoch=123" myhost:8080/singleroute
curl -F "tableName=def" -F "updEpoch=345" myhost:8080/singleroute
I want to allow multiple different tableName requests to be handled in parallel BUT only 1 request per tableName at the same time. So in above example, if above 4 requests fired at same time, then 1st and 3rd should be able to run at same time (as unique tableNames), but 2nd will only start once 1st finishes and 4th will only start once 3rd finishes. When I was researching mutex no example seemed to fit this case, I don't want to hardcode abc/def.etc anywhere in the code as same rule should apply to any arbitrary tableName.
my guess based on Crowman's help:
package main
import (
"fmt"
"sync"
"time"
"http"
)
km := KeyedMutex{}
type KeyedMutex struct {
mutexes sync.Map // Zero value is empty and ready for use
}
func (m *KeyedMutex) Lock(key string) func() {
value, _ := m.mutexes.LoadOrStore(key, &sync.Mutex{})
mtx := value.(*sync.Mutex)
mtx.Lock()
return func() { mtx.Unlock() }
}
func myFunc(key string, data string) string {
//do some stuff
return "done for key:"+key+", data: "+data
}
func main() {
key := //some form value sent to my api
data := //some form value sent to my api
unlock := km.Lock(key)
defer unlock()
retVal := myFunc(key, data)
}
You can use a sync.Map
with your table name as a key and a *sync.Mutex
as the value.
For example:
package main
import (
"fmt"
"sync"
"time"
)
type KeyedMutex struct {
mutexes sync.Map // Zero value is empty and ready for use
}
func (m *KeyedMutex) Lock(key string) func() {
value, _ := m.mutexes.LoadOrStore(key, &sync.Mutex{})
mtx := value.(*sync.Mutex)
mtx.Lock()
return func() { mtx.Unlock() }
}
func main() {
wg := sync.WaitGroup{}
km := KeyedMutex{}
for _, job := range []struct {
key string
data string
}{
{key: "abc", data: "123"},
{key: "abc", data: "456"},
{key: "def", data: "123"},
{key: "def", data: "456"},
} {
var job = job
wg.Add(1)
go func() {
defer wg.Done()
unlock := km.Lock(job.key)
defer unlock()
fmt.Printf("%s:%s mutex acquired\n", job.key, job.data)
time.Sleep(time.Second * 1) // To ensure some goroutines visibly block
fmt.Printf("%s:%s done\n", job.key, job.data)
}()
}
wg.Wait()
}
with sample output:
crow@mac:$ ./mut
def:456 mutex acquired
abc:456 mutex acquired
abc:456 done
def:456 done
abc:123 mutex acquired
def:123 mutex acquired
def:123 done
abc:123 done
crow@mac:$
to show that requests with distinct table names acquire a mutex immediately, but requests with the same table name are serialized.
You can create a package level var map[string]*sync.Mutex and lock corresponding mutex that you will get/create by table name.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With