From: nsz Date: Wed, 16 Nov 2011 11:36:33 +0000 (+0100) Subject: store simplification (use name,key->value so we can manage many k-v stores) X-Git-Url: http://nsz.repo.hu/git/?a=commitdiff_plain;h=c89b19c863fc41c0312358a866aebd425f498c76;hp=20fddcfb4f87e47d9e49eef6ed32cd3c181d6d2b;p=epoint store simplification (use name,key->value so we can manage many k-v stores) --- diff --git a/store/Makefile b/store/Makefile new file mode 100644 index 0000000..23c7767 --- /dev/null +++ b/store/Makefile @@ -0,0 +1,6 @@ +include $(GOROOT)/src/Make.inc + +TARG=epoint/store +GOFILES=store.go + +include $(GOROOT)/src/Make.pkg diff --git a/store/store.go b/store/store.go index 48614f2..2595f88 100644 --- a/store/store.go +++ b/store/store.go @@ -1,29 +1,26 @@ package store // persistent key-value store -// TODO: thread safety, persistence, efficient update and query, incremental backup +// multiple key-value store can be managed by a single db connection +// each store has a name, before usage the name of the store must be +// ensured to exist +// +// TODO: this is a toy implementation import ( - "fmt" "os" "path/filepath" "io/ioutil" - "sync" ) type Conn struct { - name string path string - mutex sync.RWMutex - memstore map[string][]byte } -func Open(name string) (c *Conn, err error) { +func Open(root string) (c *Conn, err error) { c = new(Conn) - c.memstore = make(map[string][]byte) - c.name = name - c.path, err = filepath.Abs(name) + c.path, err = filepath.Abs(root) if err != nil { return } @@ -31,49 +28,30 @@ func Open(name string) (c *Conn, err error) { if err != nil { return } - err = filepath.Walk(c.path, func(path string, info *os.FileInfo, err error) error { - if info.IsDirectory() { - if info.Name == c.name { - return nil - } - return filepath.SkipDir - } - if err != nil { - return err - } - k := info.Name - v, err := ioutil.ReadFile(filepath.Join(c.path, k)) - if err != nil { - return err - } - c.memstore[k] = v - return nil - }) return } -func (c *Conn) Get(k string) (v []byte, err error) { - c.mutex.RLock() - defer c.mutex.RUnlock() - v, ok := c.memstore[k] - if !ok { - err = fmt.Errorf("key not found") - } - return +func (c *Conn) Get(name, k string) (v []byte, err error) { + return ioutil.ReadFile(filepath.Join(c.path, name, k)) +} + +func (c *Conn) Ensure(name string) (err error) { + return os.MkdirAll(filepath.Join(c.path, name), 0755) } -func (c *Conn) Set(k string, v []byte) (err error) { - c.mutex.Lock() - defer c.mutex.Unlock() - f, err := os.Create(filepath.Join(c.path, k)) +func (c *Conn) Set(name, k string, v []byte) (err error) { + fn := filepath.Join(c.path, name, k) + // os.O_SYNC + f, err := os.Create(fn+".tmp") if err != nil { return } + defer f.Close() _, err = f.Write(v) if err != nil { return } - c.memstore[k] = v + err = os.Rename(fn+".tmp", fn) return } diff --git a/store/store_test.go b/store/store_test.go new file mode 100644 index 0000000..f36fcbb --- /dev/null +++ b/store/store_test.go @@ -0,0 +1,79 @@ +package store + +import ( + "testing" +) + +var testData = map[string]string { + "A" : "a", + "B" : "b", + "C" : "c", + "D" : "d", + "foo_bar-baz" : "a\nb\nc\nd\n", +} + +func TestStore(t *testing.T) { + c, err := Open("teststore") + defer c.Close() + if err != nil { + t.Errorf("open failed: %s", err) + return + } + err = c.Ensure("abc") + if err != nil { + t.Errorf("ensure failed: %s", err) + return + } + for k, v := range testData { + err = c.Set("abc", k, []byte(v)) + if err != nil { + t.Errorf("Set failed: %s", err) + } + } + for k, v := range testData { + d, err := c.Get("abc", k) + if err != nil { + t.Errorf("Get failed: %s", err) + continue + } + if string(d) != v { + t.Errorf("expected %s; got %s", v, string(d)) + } + } +} + +func TestPersist(t *testing.T) { + c, err := Open("teststore") + if err != nil { + t.Errorf("open failed: %s", err) + return + } + err = c.Ensure("abc") + if err != nil { + t.Errorf("ensure failed: %s", err) + return + } + for k, v := range testData { + err = c.Set("abc", k, []byte(v)) + if err != nil { + t.Errorf("Set failed: %s", err) + } + } + c.Close() + + c, err = Open("teststore") + if err != nil { + t.Errorf("open failed: %s", err) + return + } + for k, v := range testData { + d, err := c.Get("abc", k) + if err != nil { + t.Errorf("Get failed: %s", err) + continue + } + if string(d) != v { + t.Errorf("expected %s; got %s", v, string(d)) + } + } +}