Golang manipulating object inside a sync.Map

Issue

I am trying to manipulate a golang sync.Map of sync.Map, but I have some issues with the casting.
I have the following code:

func (cluster *Cluster) action(object1, object2 MyObject) {
                value, _ := cluster.globalMap.LoadOrStore(object1.name, sync.Map{})
                localMap := value.(sync.Map)
                localMap.Store(object2.Name, object2)
                
                value2, _ := cluster.resourceInflight.Load(node.Name)
                forComparison := value2.(sync.Map)
                fmt.Println(localMap.Load(object2.Name))
                fmt.Println(forComparison.Load(object2.Name))
}
{myObject  map[] map[]} true
<nil> false

I am doing this since I wish to keep the content of localMap thread safe.

The problem is I am expecting to have the same result for my two print, as "forComparison" should be pointing to the same object than "localMap". But second result is nil.

I am suspecting that the problem is coming from the casting of the interface "value" into an actual "sync.Map". But I am not sure how I can actually call the .Store method with inline casting.

I thought about Storing localMap inside cluster.globalMap, but this seems incorrect to me as it would break the whole point of using a localSyncMap and create concurrency issues.

Any input on what I should do ?

Solution

As per the comments the issue was that you were copying a sync.Map; the following code will fail (output "Not found" – playground):

var sm sync.Map
var x interface{}
x = sm
sm2 := x.(sync.Map)
sm2.Store("test", "test")
result, ok := sm.Load("test")
if ok {
    fmt.Printf("Found: %s\n", result)
} else {
    fmt.Printf("Not found\n")
}

Whereas using a pointer works as expected:

var sm sync.Map
var x interface{}
x = &sm
sm2 := x.(*sync.Map)
sm2.Store("test", "test")
result, ok := sm.Load("test")
if ok {
    fmt.Printf("Found: %s\n", result)
} else {
    fmt.Printf("Not found\n")
}

Running go vet would probably have warned you about other issues (sync.Map contains a sync.Mutex and these "must not be copied after first use").

Note that the docs for Sync.Map state:

The Map type is specialized. Most code should use a plain Go map instead, with separate locking or coordination, for better type safety and to make it easier to maintain other invariants along with the map content.

Answered By – Brits

Answer Checked By – David Marino (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.