github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/pkg/sorted/kvfile/kvfile.go (about) 1 /* 2 Copyright 2013 The Camlistore Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package kvfile provides an implementation of sorted.KeyValue 18 // on top of a single mutable database file on disk using 19 // github.com/cznic/kv. 20 package kvfile 21 22 import ( 23 "bytes" 24 "errors" 25 "fmt" 26 "io" 27 "log" 28 "os" 29 "sync" 30 31 "camlistore.org/pkg/jsonconfig" 32 "camlistore.org/pkg/sorted" 33 34 "camlistore.org/third_party/github.com/camlistore/lock" 35 "camlistore.org/third_party/github.com/cznic/kv" 36 ) 37 38 var _ sorted.Wiper = (*kvis)(nil) 39 40 func init() { 41 sorted.RegisterKeyValue("kv", NewKeyValue) 42 } 43 44 // NewStorage is a convenience that calls NewKeyValue 45 // with file as the kv storage file. 46 func NewStorage(file string) (sorted.KeyValue, error) { 47 return NewKeyValue(jsonconfig.Obj{"file": file}) 48 } 49 50 // NewKeyValue returns a KeyValue implementation on top of a 51 // github.com/cznic/kv file. 52 func NewKeyValue(cfg jsonconfig.Obj) (sorted.KeyValue, error) { 53 file := cfg.RequiredString("file") 54 if err := cfg.Validate(); err != nil { 55 return nil, err 56 } 57 createOpen := kv.Open 58 verb := "opening" 59 if _, err := os.Stat(file); os.IsNotExist(err) { 60 createOpen = kv.Create 61 verb = "creating" 62 } 63 opts := &kv.Options{ 64 Locker: func(dbname string) (io.Closer, error) { 65 lkfile := dbname + ".lock" 66 cl, err := lock.Lock(lkfile) 67 if err != nil { 68 return nil, fmt.Errorf("failed to acquire lock on %s: %v", lkfile, err) 69 } 70 return cl, nil 71 }, 72 } 73 db, err := createOpen(file, opts) 74 if err != nil { 75 return nil, fmt.Errorf("error %s %s: %v", verb, file, err) 76 } 77 is := &kvis{ 78 db: db, 79 opts: opts, 80 path: file, 81 } 82 return is, nil 83 } 84 85 type kvis struct { 86 path string 87 db *kv.DB 88 opts *kv.Options 89 txmu sync.Mutex 90 } 91 92 // TODO: use bytepool package. 93 func getBuf(n int) []byte { return make([]byte, n) } 94 func putBuf([]byte) {} 95 96 func (is *kvis) Get(key string) (string, error) { 97 buf := getBuf(200) 98 defer putBuf(buf) 99 val, err := is.db.Get(buf, []byte(key)) 100 if err != nil { 101 return "", err 102 } 103 if val == nil { 104 return "", sorted.ErrNotFound 105 } 106 return string(val), nil 107 } 108 109 func (is *kvis) Set(key, value string) error { 110 return is.db.Set([]byte(key), []byte(value)) 111 } 112 113 func (is *kvis) Delete(key string) error { 114 return is.db.Delete([]byte(key)) 115 } 116 117 func (is *kvis) Find(start, end string) sorted.Iterator { 118 it := &iter{ 119 db: is.db, 120 startKey: start, 121 endKey: []byte(end), 122 } 123 it.enum, _, it.err = it.db.Seek([]byte(start)) 124 return it 125 } 126 127 func (is *kvis) BeginBatch() sorted.BatchMutation { 128 return sorted.NewBatchMutation() 129 } 130 131 func (is *kvis) Wipe() error { 132 // Unlock the already open DB. 133 if err := is.db.Close(); err != nil { 134 return err 135 } 136 if err := os.Remove(is.path); err != nil { 137 return err 138 } 139 140 db, err := kv.Create(is.path, is.opts) 141 if err != nil { 142 return fmt.Errorf("error creating %s: %v", is.path, err) 143 } 144 is.db = db 145 return nil 146 } 147 148 type batch interface { 149 Mutations() []sorted.Mutation 150 } 151 152 func (is *kvis) CommitBatch(bm sorted.BatchMutation) error { 153 b, ok := bm.(batch) 154 if !ok { 155 return errors.New("invalid batch type") 156 } 157 is.txmu.Lock() 158 defer is.txmu.Unlock() 159 160 good := false 161 defer func() { 162 if !good { 163 is.db.Rollback() 164 } 165 }() 166 167 if err := is.db.BeginTransaction(); err != nil { 168 return err 169 } 170 for _, m := range b.Mutations() { 171 if m.IsDelete() { 172 if err := is.db.Delete([]byte(m.Key())); err != nil { 173 return err 174 } 175 } else { 176 if err := is.db.Set([]byte(m.Key()), []byte(m.Value())); err != nil { 177 return err 178 } 179 } 180 } 181 182 good = true 183 return is.db.Commit() 184 } 185 186 func (is *kvis) Close() error { 187 log.Printf("Closing kvfile database %s", is.path) 188 return is.db.Close() 189 } 190 191 type iter struct { 192 db *kv.DB 193 startKey string 194 endKey []byte 195 196 enum *kv.Enumerator 197 198 valid bool 199 key, val []byte 200 skey, sval *string // non-nil if valid 201 202 err error 203 closed bool 204 } 205 206 func (it *iter) Close() error { 207 it.closed = true 208 return it.err 209 } 210 211 func (it *iter) KeyBytes() []byte { 212 if !it.valid { 213 panic("not valid") 214 } 215 return it.key 216 } 217 218 func (it *iter) Key() string { 219 if !it.valid { 220 panic("not valid") 221 } 222 if it.skey != nil { 223 return *it.skey 224 } 225 str := string(it.key) 226 it.skey = &str 227 return str 228 } 229 230 func (it *iter) ValueBytes() []byte { 231 if !it.valid { 232 panic("not valid") 233 } 234 return it.val 235 } 236 237 func (it *iter) Value() string { 238 if !it.valid { 239 panic("not valid") 240 } 241 if it.sval != nil { 242 return *it.sval 243 } 244 str := string(it.val) 245 it.sval = &str 246 return str 247 } 248 249 func (it *iter) end() bool { 250 it.valid = false 251 it.closed = true 252 return false 253 } 254 255 func (it *iter) Next() (ret bool) { 256 if it.err != nil { 257 return false 258 } 259 if it.closed { 260 panic("Next called after Next returned value") 261 } 262 it.skey, it.sval = nil, nil 263 var err error 264 it.key, it.val, err = it.enum.Next() 265 if err == io.EOF { 266 it.err = nil 267 return it.end() 268 } 269 if err != nil { 270 it.err = err 271 return it.end() 272 } 273 if len(it.endKey) > 0 && bytes.Compare(it.key, it.endKey) >= 0 { 274 return it.end() 275 } 276 it.valid = true 277 return true 278 }