sync.RWMutex and the dispatch table
Before the code: what a key-value store actually is
So far the server understands the Redis language but has nothing to remember. Time to give it a brain. The brain is a Go map that lives in memory, plus a small companion table of timers for keys that should disappear on their own. That is the whole Redis storage model, in plain English. The rest of this module wires SET, GET, DEL, and EXPIRE into that map, and uses sync.RWMutex so many readers can run while writes stay exclusive.
The store, with and without timers
Quiz: Quiz
Loading practice…
Once we have goroutines we have shared state. Go maps are not safe for concurrent access. RWMutex is the right tool when reads outnumber writes, like Redis-shaped workloads.
type Store struct {
mu sync.RWMutex
m map[string]string
}
func (s *Store) Get(key string) (string, bool) {
s.mu.RLock()
defer s.mu.RUnlock()
v, ok := s.m[key]
return v, ok
}
func (s *Store) Set(key, value string) {
s.mu.Lock()
defer s.mu.Unlock()
s.m[key] = value
}Store struct wraps the map and the mutex together. Get takes RLock, Set takes Lock.
type Handler func(args []string) []byte
var commands = map[string]Handler{
"PING": func(args []string) []byte {
if len(args) == 0 { return encodeSimple("PONG") }
return encodeBulk(args[0], false)
},
"SET": func(args []string) []byte {
if len(args) < 2 { return encodeError("...") }
store.Set(args[0], args[1])
return encodeSimple("OK")
},
"GET": func(args []string) []byte { /* ... */ },
}map[string]Handler. The Go translation of the Python dispatch dict. Same uniform handler signature.
Matching exercise: Pick the right concurrency primitive
Loading practice…