leveldb_helper.go 4.3 KB
Newer Older
manish's avatar
manish committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
Copyright IBM Corp. 2016 All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

		 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

manish's avatar
manish committed
17
package leveldbhelper
manish's avatar
manish committed
18
19
20
21
22

import (
	"fmt"
	"sync"

23
	"github.com/hyperledger/fabric/common/ledger/util"
manish's avatar
manish committed
24
	"github.com/op/go-logging"
manish's avatar
manish committed
25
	"github.com/syndtr/goleveldb/leveldb"
manish's avatar
manish committed
26
	"github.com/syndtr/goleveldb/leveldb/iterator"
manish's avatar
manish committed
27
	"github.com/syndtr/goleveldb/leveldb/opt"
manish's avatar
manish committed
28
	goleveldbutil "github.com/syndtr/goleveldb/leveldb/util"
manish's avatar
manish committed
29
30
)

manish's avatar
manish committed
31
var logger = logging.MustGetLogger("leveldbhelper")
manish's avatar
manish committed
32
33
34
35

type dbState int32

const (
manish's avatar
manish committed
36
	closed dbState = iota
manish's avatar
manish committed
37
38
39
40
41
	opened
)

// Conf configuration for `DB`
type Conf struct {
manish's avatar
manish committed
42
	DBPath string
manish's avatar
manish committed
43
44
}

manish's avatar
manish committed
45
// DB - a wrapper on an actual store
manish's avatar
manish committed
46
type DB struct {
manish's avatar
manish committed
47
48
49
50
51
52
53
54
	conf    *Conf
	db      *leveldb.DB
	dbState dbState
	mux     sync.Mutex

	readOpts        *opt.ReadOptions
	writeOptsNoSync *opt.WriteOptions
	writeOptsSync   *opt.WriteOptions
manish's avatar
manish committed
55
56
57
58
}

// CreateDB constructs a `DB`
func CreateDB(conf *Conf) *DB {
manish's avatar
manish committed
59
60
61
62
63
	readOpts := &opt.ReadOptions{}
	writeOptsNoSync := &opt.WriteOptions{}
	writeOptsSync := &opt.WriteOptions{}
	writeOptsSync.Sync = true

manish's avatar
manish committed
64
	return &DB{
manish's avatar
manish committed
65
66
67
68
69
		conf:            conf,
		dbState:         closed,
		readOpts:        readOpts,
		writeOptsNoSync: writeOptsNoSync,
		writeOptsSync:   writeOptsSync}
manish's avatar
manish committed
70
71
}

manish's avatar
manish committed
72
// Open opens the underlying db
manish's avatar
manish committed
73
74
func (dbInst *DB) Open() {
	dbInst.mux.Lock()
manish's avatar
manish committed
75
	defer dbInst.mux.Unlock()
manish's avatar
manish committed
76
77
78
	if dbInst.dbState == opened {
		return
	}
manish's avatar
manish committed
79
	dbOpts := &opt.Options{}
manish's avatar
manish committed
80
	dbPath := dbInst.conf.DBPath
manish's avatar
manish committed
81
82
83
	var err error
	var dirEmpty bool
	if dirEmpty, err = util.CreateDirIfMissing(dbPath); err != nil {
84
		panic(fmt.Sprintf("Error while trying to create dir if missing: %s", err))
manish's avatar
manish committed
85
	}
manish's avatar
manish committed
86
87
88
	dbOpts.ErrorIfMissing = !dirEmpty
	if dbInst.db, err = leveldb.OpenFile(dbPath, dbOpts); err != nil {
		panic(fmt.Sprintf("Error while trying to open DB: %s", err))
manish's avatar
manish committed
89
90
91
92
	}
	dbInst.dbState = opened
}

manish's avatar
manish committed
93
// Close closes the underlying db
manish's avatar
manish committed
94
95
func (dbInst *DB) Close() {
	dbInst.mux.Lock()
manish's avatar
manish committed
96
	defer dbInst.mux.Unlock()
manish's avatar
manish committed
97
98
99
	if dbInst.dbState == closed {
		return
	}
100
101
102
	if err := dbInst.db.Close(); err != nil {
		logger.Errorf("Error while closing DB: %s", err)
	}
manish's avatar
manish committed
103
104
105
106
107
108
109
110
111
	dbInst.dbState = closed
}

func (dbInst *DB) isOpen() bool {
	dbInst.mux.Lock()
	defer dbInst.mux.Unlock()
	return dbInst.dbState == opened
}

manish's avatar
manish committed
112
113
114
115
// Get returns the value for the given key
func (dbInst *DB) Get(key []byte) ([]byte, error) {
	value, err := dbInst.db.Get(key, dbInst.readOpts)
	if err == leveldb.ErrNotFound {
116
		value = nil
manish's avatar
manish committed
117
118
		err = nil
	}
manish's avatar
manish committed
119
	if err != nil {
manish's avatar
manish committed
120
		logger.Errorf("Error while trying to retrieve key [%#v]: %s", key, err)
manish's avatar
manish committed
121
122
		return nil, err
	}
manish's avatar
manish committed
123
	return value, nil
manish's avatar
manish committed
124
125
}

manish's avatar
manish committed
126
127
128
129
130
131
132
// Put saves the key/value
func (dbInst *DB) Put(key []byte, value []byte, sync bool) error {
	wo := dbInst.writeOptsNoSync
	if sync {
		wo = dbInst.writeOptsSync
	}
	err := dbInst.db.Put(key, value, wo)
manish's avatar
manish committed
133
	if err != nil {
manish's avatar
manish committed
134
		logger.Errorf("Error while trying to write key [%#v]", key)
manish's avatar
manish committed
135
136
137
138
139
		return err
	}
	return nil
}

manish's avatar
manish committed
140
141
142
143
144
145
146
// Delete deletes the given key
func (dbInst *DB) Delete(key []byte, sync bool) error {
	wo := dbInst.writeOptsNoSync
	if sync {
		wo = dbInst.writeOptsSync
	}
	err := dbInst.db.Delete(key, wo)
manish's avatar
manish committed
147
	if err != nil {
manish's avatar
manish committed
148
		logger.Errorf("Error while trying to delete key [%#v]", key)
manish's avatar
manish committed
149
150
151
152
153
		return err
	}
	return nil
}

manish's avatar
manish committed
154
155
156
// GetIterator returns an iterator over key-value store. The iterator should be released after the use.
// The resultset contains all the keys that are present in the db between the startKey (inclusive) and the endKey (exclusive).
// A nil startKey represents the first available key and a nil endKey represent a logical key after the last available key
manish's avatar
manish committed
157
158
159
160
func (dbInst *DB) GetIterator(startKey []byte, endKey []byte) iterator.Iterator {
	return dbInst.db.NewIterator(&goleveldbutil.Range{Start: startKey, Limit: endKey}, dbInst.readOpts)
}

manish's avatar
manish committed
161
// WriteBatch writes a batch
manish's avatar
manish committed
162
163
164
165
166
167
func (dbInst *DB) WriteBatch(batch *leveldb.Batch, sync bool) error {
	wo := dbInst.writeOptsNoSync
	if sync {
		wo = dbInst.writeOptsSync
	}
	if err := dbInst.db.Write(batch, wo); err != nil {
manish's avatar
manish committed
168
169
170
171
		return err
	}
	return nil
}