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
}
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)
+ f, err := os.OpenFile(fn+".tmp", os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_SYNC, 0666)
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
+}
+
+func (c *Conn) Append(name, k string, v []byte) (err error) {
+ fn := filepath.Join(c.path, name, k)
+ f, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.O_WRONLY|os.O_SYNC, 0666)
+ if err != nil {
+ return
+ }
+ defer f.Close()
+ _, err = f.Write(v)
return
}