Skip to content

Instantly share code, notes, and snippets.

@tjstebbing
Last active January 17, 2019 04:43
Show Gist options
  • Select an option

  • Save tjstebbing/4eb3ee24faacee477a47c17c750cb829 to your computer and use it in GitHub Desktop.

Select an option

Save tjstebbing/4eb3ee24faacee477a47c17c750cb829 to your computer and use it in GitHub Desktop.

Atomic refreshing in memory DBs without mutexes

This is a simple way of having an in-memory DB which is safe to use across multiple goroutines without needing to use any kind of mutex for updates.

1. Create an interface

type FooDB interface {
  Refresh() // Update the database
  GetFoo(id int) (Foo, error)
}

2. Implement internal DB using a pointer

type InMemFooDB struct {
  Ready chan bool
  db    *map[int]Foo // whatever format your data takes
}

func NewInMemFooDB() FooDB {
  db := map[int]Foo
  fdb := InMemFooDB{make(chan bool), &db}
  // trigger refresh so it loads at the start
  fdb.Refresh()
  return fdb
}

func (t *InMemFooDB) GetFoo(id int) (Foo, error) {
  if foo, ok := t.db[id]; ok {
    return foo
  }
  return errors.New(fmt.Sprintf("'%s' was not found in db", id))
}

func (t *InMemFooDB) Refresh() {
  go func() {
    newDB := map[int]Foo{} // create a new 'db' value in a goroutine
    
    // do long running thing that slowly updates newDB, meanwhile
    // GetFoo continues to operate on the current data..
    
    t.db = &newDB // Atomic switch from one complete DB to another
    close(t.Ready) // indicate that we are ready for business (first run)
  }
}

3. Create one and pass it around at startup, schedule Refresh however you like

db := NewInMemFooDB()
<- db.Ready //wait for it to load

db.GetFoo(123) 
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment