11/28/2013 - 12:23 AM

A Goroutine safe pattern using channels that abstracts away the usage of channels.

A Goroutine safe pattern using channels that abstracts away the usage of channels.

  A Goroutine safe pattern using channels that abstracts away the channels

  This is a concept of creating a goroutine safe object that uses channels under the covers to communicate
  with the internal map[string]string structure.  I know that typically this kind of solution may done
  with mutexes but the excercise was in using channels on purpose although they are heavier.

  Note a couple of points:

  - When using channels, you can still build a public-facing api that nicely abstracts them away, therefore
    somemone using this api for example, doesn't have to understand the paradigm of communicating over channels
  - This example is just a prototype, as an example
  - Notice that all state is mutated internal to the Db's f function
  - Notice that in the Fetch method there is bi-directional communication a send/receive

package main

import "fmt"
import "time"

type KeyValue struct {
	Key   string
	Value string
	Reply chan KeyValue

type Db struct {
	db           map[string]string
	storeChannel chan KeyValue
	fetchChannel chan KeyValue

func NewDb() *Db {
	d := &Db{}

	d.db = make(map[string]string)

	d.storeChannel = make(chan KeyValue)
	d.fetchChannel = make(chan KeyValue)

	go func() {

		for {
			select {
			case storeValue := <-d.storeChannel:

			case fetchKey := <-d.fetchChannel:
				fetchKey.Reply <- d.internalFetch(fetchKey)

	return d

func (d *Db) internalFetch(kv KeyValue) KeyValue {
	v, ok := d.db[kv.Key]
	if ok {
		return KeyValue{Key: kv.Key, Value: v}
	return KeyValue{Key: kv.Key}

func (d *Db) internalStore(kv KeyValue) {
	d.db[kv.Key] = kv.Value
	fmt.Println("Just stored: ", kv)

func (d *Db) Fetch(key string) KeyValue {

	ch := make(chan KeyValue)
	d.fetchChannel <- KeyValue{Key: key, Reply: ch}

	return <-ch

func (d *Db) Store(key string, value string) {

	d.storeChannel <- KeyValue{Key: key, Value: value}

func main() {

	myDb := NewDb()

	//myDb can safely be used by many goroutines although in this example it's only used by the main goroutine.
	myDb.Store("id-3383", "John")
	myDb.Store("id-2218", "Ralph")
	myDb.Store("id-7741", "Amy")

	//simulate some time has gone by
	time.Sleep(time.Second * 1)


	//not found returns a KeyValue with an empty value

	var s string