blob: 672eb8349777f36123c4685801230ca475ff6007 [file] [log] [blame]
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -07005// Package leveldb provides a LevelDB-based implementation of store.Store.
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -07006package leveldb
7
8// #cgo LDFLAGS: -lleveldb
9// #include <stdlib.h>
10// #include "leveldb/c.h"
11// #include "syncbase_leveldb.h"
12import "C"
13import (
Sergey Rogulenko0dbfe072015-05-19 20:10:18 -070014 "errors"
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -070015 "sync"
16 "unsafe"
17
18 "v.io/syncbase/x/ref/services/syncbase/store"
19)
20
Sergey Rogulenko0dbfe072015-05-19 20:10:18 -070021var (
22 errClosedStore = errors.New("closed store")
23)
24
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -070025// db is a wrapper around LevelDB that implements the store.Store interface.
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -070026type db struct {
Sergey Rogulenko0dbfe072015-05-19 20:10:18 -070027 // mu protects cDb.
28 mu sync.RWMutex
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -070029 cDb *C.leveldb_t
30 // Default read/write options.
31 readOptions *C.leveldb_readoptions_t
32 writeOptions *C.leveldb_writeoptions_t
Sergey Rogulenko0dbfe072015-05-19 20:10:18 -070033 err error
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -070034 // Used to prevent concurrent transactions.
35 // TODO(rogulenko): improve concurrency.
Sergey Rogulenko0dbfe072015-05-19 20:10:18 -070036 txmu sync.Mutex
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -070037}
38
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -070039var _ store.Store = (*db)(nil)
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -070040
41// Open opens the database located at the given path, creating it if it doesn't
42// exist.
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -070043func Open(path string) (store.Store, error) {
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -070044 var cError *C.char
45 cPath := C.CString(path)
46 defer C.free(unsafe.Pointer(cPath))
47
48 cOpts := C.leveldb_options_create()
49 C.leveldb_options_set_create_if_missing(cOpts, 1)
50 C.leveldb_options_set_paranoid_checks(cOpts, 1)
51 defer C.leveldb_options_destroy(cOpts)
52
53 cDb := C.leveldb_open(cOpts, cPath, &cError)
54 if err := goError(cError); err != nil {
55 return nil, err
56 }
57 readOptions := C.leveldb_readoptions_create()
58 C.leveldb_readoptions_set_verify_checksums(readOptions, 1)
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -070059 return &db{
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -070060 cDb: cDb,
61 readOptions: readOptions,
62 writeOptions: C.leveldb_writeoptions_create(),
63 }, nil
64}
65
66// Close implements the store.Store interface.
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -070067func (d *db) Close() error {
Sergey Rogulenko0dbfe072015-05-19 20:10:18 -070068 d.mu.Lock()
69 defer d.mu.Unlock()
70 if d.err != nil {
71 return d.err
72 }
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -070073 C.leveldb_close(d.cDb)
Sergey Rogulenko0dbfe072015-05-19 20:10:18 -070074 d.cDb = nil
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -070075 C.leveldb_readoptions_destroy(d.readOptions)
Sergey Rogulenko0dbfe072015-05-19 20:10:18 -070076 d.readOptions = nil
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -070077 C.leveldb_writeoptions_destroy(d.writeOptions)
Sergey Rogulenko0dbfe072015-05-19 20:10:18 -070078 d.writeOptions = nil
79 d.err = errors.New("closed store")
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -070080 return nil
81}
82
83// Destroy removes all physical data of the database located at the given path.
84func Destroy(path string) error {
85 var cError *C.char
86 cPath := C.CString(path)
87 defer C.free(unsafe.Pointer(cPath))
88 cOpts := C.leveldb_options_create()
89 defer C.leveldb_options_destroy(cOpts)
90 C.leveldb_destroy_db(cOpts, cPath, &cError)
91 return goError(cError)
92}
93
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -070094// Get implements the store.StoreReader interface.
95func (d *db) Get(key, valbuf []byte) ([]byte, error) {
96 return d.getWithOpts(key, valbuf, d.readOptions)
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -070097}
98
99// Scan implements the store.StoreReader interface.
Sergey Rogulenko0dbfe072015-05-19 20:10:18 -0700100func (d *db) Scan(start, end []byte) store.Stream {
101 d.mu.RLock()
102 defer d.mu.RUnlock()
103 if d.err != nil {
104 return &store.InvalidStream{d.err}
105 }
106 return newStream(d, start, end, d.readOptions)
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -0700107}
108
109// Put implements the store.StoreWriter interface.
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -0700110func (d *db) Put(key, value []byte) error {
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -0700111 // TODO(rogulenko): improve performance.
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -0700112 return store.RunInTransaction(d, func(st store.StoreReadWriter) error {
Sergey Rogulenko802fe1e2015-05-08 12:51:22 -0700113 return st.Put(key, value)
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -0700114 })
115}
116
117// Delete implements the store.StoreWriter interface.
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -0700118func (d *db) Delete(key []byte) error {
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -0700119 // TODO(rogulenko): improve performance.
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -0700120 return store.RunInTransaction(d, func(st store.StoreReadWriter) error {
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -0700121 return st.Delete(key)
122 })
123}
124
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -0700125// NewTransaction implements the store.Store interface.
126func (d *db) NewTransaction() store.Transaction {
Sergey Rogulenko0dbfe072015-05-19 20:10:18 -0700127 // txmu is held until the transaction is successfully committed or aborted.
128 d.txmu.Lock()
129 d.mu.RLock()
130 defer d.mu.RUnlock()
131 if d.err != nil {
132 d.txmu.Unlock()
133 return &store.InvalidTransaction{d.err}
134 }
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -0700135 return newTransaction(d)
136}
137
138// NewSnapshot implements the store.Store interface.
139func (d *db) NewSnapshot() store.Snapshot {
Sergey Rogulenko0dbfe072015-05-19 20:10:18 -0700140 d.mu.RLock()
141 defer d.mu.RUnlock()
142 if d.err != nil {
143 return &store.InvalidSnapshot{d.err}
144 }
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -0700145 return newSnapshot(d)
146}
147
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -0700148// getWithOpts returns the value for the given key.
149// cOpts may contain a pointer to a snapshot.
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -0700150func (d *db) getWithOpts(key, valbuf []byte, cOpts *C.leveldb_readoptions_t) ([]byte, error) {
Sergey Rogulenko0dbfe072015-05-19 20:10:18 -0700151 d.mu.RLock()
152 defer d.mu.RUnlock()
153 if d.err != nil {
154 return valbuf, d.err
155 }
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -0700156 var cError *C.char
157 var valLen C.size_t
158 cStr, cLen := cSlice(key)
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -0700159 val := C.leveldb_get(d.cDb, cOpts, cStr, cLen, &valLen, &cError)
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -0700160 if err := goError(cError); err != nil {
Sergey Rogulenko0dbfe072015-05-19 20:10:18 -0700161 return valbuf, err
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -0700162 }
163 if val == nil {
Sergey Rogulenko0dbfe072015-05-19 20:10:18 -0700164 return valbuf, &store.ErrUnknownKey{Key: string(key)}
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -0700165 }
166 defer C.leveldb_free(unsafe.Pointer(val))
Adam Sadovskyc18c8ca2015-05-08 18:05:46 -0700167 return store.CopyBytes(valbuf, goBytes(val, valLen)), nil
Sergey Rogulenkob0081cf2015-05-05 22:39:37 -0700168}