Kulcs-érték tár REST API-val: kvault
Néha kell egy egyszerű kulcs-érték tár. Nem Redis, nem PostgreSQL – csak egy hely ahol egy API kulcsot, egy konfigurációs értéket, vagy egy ideiglenes adatot el tudsz rakni és HTTP-n eléred.
A kvault pontosan ennyi. Egy JSON fájlban tárolja az adatot, és REST API-n keresztül kezelhető: GET, POST, DELETE. Induláskor visszatölti a fájlt a memóriába, minden írásnál elmenti.
CRUD egy handler-ben
Az eddigi projektek végpontjai mind egyetlen HTTP metódust kezeltek. A kvault /keys/{key} végpontja háromfélét: GET olvas, POST ír, DELETE töröl. A switch r.Method elágazás dönti el mit csináljon:
switch r.Method {
case http.MethodGet:
// olvasás
case http.MethodPost:
// írás
case http.MethodDelete:
// törlés
default:
// 405
}
Ez a REST minta alapja – ugyanaz az URL, különböző metódusok, különböző viselkedés. Az envcheck-ben flag-ek voltak, a deployer-ben subcommand-ok, a kvault-ban HTTP metódusok. Háromféle interfész, de a gondolkodásmód ugyanaz: a bemenet határozza meg a műveletet.
RWMutex
Az eredeti verzió globális map-et használt zárolás nélkül. Egy felhasználónál ez működik, de ha két kérés egyszerre ír, a Go race detector hibát jelez.
A production ready verzió sync.RWMutex-et használ:
func (s *fileStore) get(key string) (string, bool) {
s.mu.RLock()
defer s.mu.RUnlock()
v, ok := s.data[key]
return v, ok
}
Az RLock olvasási zárolás – több goroutine egyszerre olvashat. A Lock írási zárolás – kizárólagos, sem olvasás sem másik írás nem történhet közben. Ez finomabb mint a netmapper sima Mutex-e, mert az olvasásokat nem lassítja.
A storage interface
A teszteléshez egy storage interface absztrahálja az adatkezelést:
type storage interface {
get(key string) (string, bool)
set(key, value string)
del(key string) bool
all() map[string]string
count() int
}
A valódi fileStore JSON fájlba ment. A teszt memStore csak memóriában tartja az adatot. A handler-ek a storage interface-t kapják – nem tudják és nem is érdekli őket hogy mi van mögötte.
Ez már a negyedik projekt ahol interface-t használunk teszteléshez: deployer (runner), sshping (dialer), hookrelay (logger), kvault (storage). A minta ugyanaz, az absztrakció más.
Fájlperzisztencia tesztelés
A fileStore tesztje két store-t hoz létre ugyanarra a temp fájlra:
s1 := newFileStore(path)
s1.set("key1", "value1")
s2 := newFileStore(path)
v, _ := s2.get("key1") // "value1"
Az első store ír, a második olvas – és megtalálja amit az első mentett. Ez bizonyítja hogy az adat ténylegesen a fájlba került, nem csak a memóriában volt.
Mit tanultam?
A kvault két Go koncepciót hozott ami az eddigi projektekben nem volt:
Az RWMutex – olvasási és írási zárolás szétválasztása. Olvasni többen is tudnak egyszerre, írni csak egy. Ez a valódi szerver alkalmazások alapja.
A teljes CRUD ciklus egyetlen handler-ben – hogyan kezeld a különböző HTTP metódusokat, hogyan adj vissza megfelelő státusz kódokat (200, 201, 400, 404, 405), és hogyan tartsd konzisztensnek az API-t.
Hogyan próbáld ki?
git clone https://github.com/brtkcs/werkstatt-tools.git
cd werkstatt-tools/kvault
go run main.go
Egy másik terminálból:
curl -X POST localhost:8080/keys/secret -d '{"value":"mypassword"}'
curl localhost:8080/keys/secret
curl localhost:8080/keys
curl -X DELETE localhost:8080/keys/secret
A teljes forráskód a werkstatt-tools repóban.