Caching is the go-to solution in applications to avoid repeating expensive computation and instead prefer some value that can be readily fetched in-memory. A simple caching strategy is to use a cache as a thin layer above database read access as follows:
package main
import "sync"
type Database struct {
	cache map[string][]byte
	lock  sync.RWMutex
}
func (db *Database) GetItem(key []byte) ([]byte, error) {
	db.lock.RLock()
	if value, ok := db.cache[string(key)]; ok {
		db.lock.RUnlock()
		return value
	}
	db.lock.RUnlock()
	return db.readFromDatabase(key)
}
func (db *Database) WriteItem(key, value []byte) error {
	if err := db.writeToDatabase(key, value); err != nil {
		return err
	}
	db.lock.Lock()
	db.cache[string(key)] = value
	db.lock.Unlock()
	return nil
}
This strategy works great for applications where you have requests to read access for a certain value repeatedly, preventing you from performing a potentially expensive db query and leveraging fast access in-memory. Caching is very helpful. For some problems, however, a cache is definitely not enough.


