3 // persistent key-value store
4 // multiple key-value store can be managed by a single db connection
5 // each store has a name, before usage the name of the store must be
8 // TODO: this is a toy implementation
20 type NotFoundError struct {
24 type AlreadyExistsError struct {
28 func (e NotFoundError) Error() string {
29 return "not found: " + e.Path
32 func (e AlreadyExistsError) Error() string {
33 return "already exists: " + e.Path
37 func Open(root string) (c *Conn, err error) {
39 c.path, err = filepath.Abs(root)
43 fn := filepath.Join(c.path, ".journal")
44 err = os.MkdirAll(fn, 0755)
51 // TODO: list .journal for recovery after a crash
54 func (c *Conn) Get(name, k string) (v []byte, err error) {
55 v, err = ioutil.ReadFile(filepath.Join(c.path, name, k))
57 if p, ok := err.(*os.PathError); ok && p.Err == os.ENOENT {
58 err = NotFoundError{name + "/" + k}
64 // Ensure k-v store with the given name exists
65 func (c *Conn) Ensure(name string) (err error) {
66 return os.MkdirAll(filepath.Join(c.path, name), 0755)
70 func (c *Conn) Set(name, k string, v []byte) (err error) {
71 fn := filepath.Join(c.path, name, k)
72 f, err := os.OpenFile(fn+".tmp", os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_SYNC, 0666)
81 err = os.Rename(fn+".tmp", fn)
85 // Set k to v, but fail if k already exists
86 func (c *Conn) Insert(name, k string, v []byte) (err error) {
87 fn := filepath.Join(c.path, name, k)
88 f, err := os.OpenFile(fn, os.O_CREATE|os.O_EXCL|os.O_WRONLY|os.O_SYNC, 0666)
90 if p, ok := err.(*os.PathError); ok && p.Err == os.EEXIST {
91 err = AlreadyExistsError{name + "/" + k}
100 // Append v to value of k
101 func (c *Conn) Append(name, k string, v []byte) (err error) {
102 fn := filepath.Join(c.path, name, k)
103 f, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.O_WRONLY|os.O_SYNC, 0666)
112 // Begin transaction identified by k
113 func (c *Conn) Begin(k string) (err error) {
114 fn := filepath.Join(c.path, ".journal", k)
115 f, err := os.OpenFile(fn, os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_SYNC, 0666)
123 // End transaction identified by k
124 func (c *Conn) End(k string) (err error) {
125 fn := filepath.Join(c.path, ".journal", k)
130 // Close db connection
131 func (c *Conn) Close() (err error) {