Implementation for Copy-On-Write

1,368 views
Skip to first unread message

ag9920

unread,
Jun 9, 2022, 12:41:18 PM6/9/22
to golang-nuts
Copy-On-Write is a classic way to deal with concurrency problems in the "frequently read, but infrequently updated" scenario.

I noticed an interesting discussion from a previous conversation: https://groups.google.com/g/golang-nuts/c/zyQnord8hyc. The author of weed-fs tried to implement Copy-On-Write by simply assigning a new value to a string variable in a struct. Like this: `vs.masterNode = master`.

So here comes the question, what's the recommend way of implmentating COW?

The atomic package provides an example: https://pkg.go.dev/sync/atomic#example-Value-ReadMostly

import (
  "sync"
  "sync/atomic"
)

func main() {
  type Map map[string]string
  var m atomic.Value
  m.Store(make(Map))
  var mu sync.Mutex // used only by writers
  // read function can be used to read the data without further synchronization
  read := func(key string) (val string) {
    m1 := m.Load().(Map)
    return m1[key]
  }
  // insert function can be used to update the data without further synchronization
  insert := func(key, val string) {
    mu.Lock() // synchronize with other potential writers
    defer mu.Unlock()
    m1 := m.Load().(Map) // load current value of the data structure
    m2 := make(Map)      // create a new value
    for k, v := range m1 {
      m2[k] = v // copy all data from the current object to the new one
    }
    m2[key] = val // do the update that we need
    m.Store(m2)   // atomically replace the current object with the new one
    // At this point all new readers start working with the new version.
    // The old version will be garbage collected once the existing readers
    // (if any) are done with it.
  }
  _, _ = read, insert
}


Acutally, most of the usecase I saw use map as the underlying data structure for demonstration.

What's the correct way to implement COW on a string variable, or maybe other primitive type like int64 ? Should I just replace the `Map` type, and keep everything else the same in the above official example ?

Brian Candler

unread,
Jun 9, 2022, 5:11:33 PM6/9/22
to golang-nuts
You can trim most of it out. Instead of the 'insert' func, just m.Store() the new value.

But that isn't Copy-On-Write.  This is simply replacing the value in a variable with another value.  Anybody who read out the old value, still has their copy of the old value, probably stored in some variable of their own.  So really this is copy-on-read, not copy-on-write.

In any case, string and int64 are both immutable types, so you don't have to worry whether it's a copy or not.

Note that gotip has extra types like atomic.Int64, to make it easier to work with such values (and safer, because there's no way to access them accidentally without going through the synchronization interface). See: https://pkg.go.dev/sync/atomic@master
Reply all
Reply to author
Forward
0 new messages