fix balance check in server logic
[epoint] / pkg / store / store.go
1 package store
2
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
6 // ensured to exist
7 //
8 // TODO: this is a toy implementation
9
10 import (
11         "io/ioutil"
12         "os"
13         "path/filepath"
14 )
15
16 type Conn struct {
17         path string
18 }
19
20 type NotFoundError struct {
21         Path string
22 }
23
24 type AlreadyExistsError struct {
25         Path string
26 }
27
28 func (e NotFoundError) Error() string {
29         return "not found: " + e.Path
30 }
31
32 func (e AlreadyExistsError) Error() string {
33         return "already exists: " + e.Path
34 }
35
36 // Open db connection
37 func Open(root string) (c *Conn, err error) {
38         c = new(Conn)
39         c.path, err = filepath.Abs(root)
40         if err != nil {
41                 return
42         }
43         fn := filepath.Join(c.path, ".journal")
44         err = os.MkdirAll(fn, 0755)
45         if err != nil {
46                 return
47         }
48         return
49 }
50
51 // TODO: list .journal for recovery after a crash
52
53 // Get the value of k
54 func (c *Conn) Get(name, k string) (v []byte, err error) {
55         v, err = ioutil.ReadFile(filepath.Join(c.path, name, k))
56         if err != nil {
57                 if p, ok := err.(*os.PathError); ok && p.Err == os.ENOENT {
58                         err = NotFoundError{name + "/" + k}
59                 }
60         }
61         return
62 }
63
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)
67 }
68
69 // Set k to v
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)
73         if err != nil {
74                 return
75         }
76         defer f.Close()
77         _, err = f.Write(v)
78         if err != nil {
79                 return
80         }
81         err = os.Rename(fn+".tmp", fn)
82         return
83 }
84
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)
89         if err != nil {
90                 if p, ok := err.(*os.PathError); ok && p.Err == os.EEXIST {
91                         err = AlreadyExistsError{name + "/" + k}
92                 }
93                 return
94         }
95         defer f.Close()
96         _, err = f.Write(v)
97         return
98 }
99
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)
104         if err != nil {
105                 return
106         }
107         defer f.Close()
108         _, err = f.Write(v)
109         return
110 }
111
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)
116         if err != nil {
117                 return
118         }
119         err = f.Close()
120         return
121 }
122
123 // End transaction identified by k
124 func (c *Conn) End(k string) (err error) {
125         fn := filepath.Join(c.path, ".journal", k)
126         err = os.Remove(fn)
127         return
128 }
129
130 // Close db connection
131 func (c *Conn) Close() (err error) {
132         return
133 }