You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

349 lines
7.3 KiB

  1. // +build boltdb
  2. package db
  3. import (
  4. "bytes"
  5. "errors"
  6. "fmt"
  7. "os"
  8. "path/filepath"
  9. "github.com/etcd-io/bbolt"
  10. )
  11. var bucket = []byte("tm")
  12. func init() {
  13. registerDBCreator(BoltDBBackend, func(name, dir string) (DB, error) {
  14. return NewBoltDB(name, dir)
  15. }, false)
  16. }
  17. // BoltDB is a wrapper around etcd's fork of bolt
  18. // (https://github.com/etcd-io/bbolt).
  19. //
  20. // NOTE: All operations (including Set, Delete) are synchronous by default. One
  21. // can globally turn it off by using NoSync config option (not recommended).
  22. //
  23. // A single bucket ([]byte("tm")) is used per a database instance. This could
  24. // lead to performance issues when/if there will be lots of keys.
  25. type BoltDB struct {
  26. db *bbolt.DB
  27. }
  28. // NewBoltDB returns a BoltDB with default options.
  29. func NewBoltDB(name, dir string) (DB, error) {
  30. return NewBoltDBWithOpts(name, dir, bbolt.DefaultOptions)
  31. }
  32. // NewBoltDBWithOpts allows you to supply *bbolt.Options. ReadOnly: true is not
  33. // supported because NewBoltDBWithOpts creates a global bucket.
  34. func NewBoltDBWithOpts(name string, dir string, opts *bbolt.Options) (DB, error) {
  35. if opts.ReadOnly {
  36. return nil, errors.New("ReadOnly: true is not supported")
  37. }
  38. dbPath := filepath.Join(dir, name+".db")
  39. db, err := bbolt.Open(dbPath, os.ModePerm, opts)
  40. if err != nil {
  41. return nil, err
  42. }
  43. // create a global bucket
  44. err = db.Update(func(tx *bbolt.Tx) error {
  45. _, err := tx.CreateBucketIfNotExists(bucket)
  46. return err
  47. })
  48. if err != nil {
  49. return nil, err
  50. }
  51. return &BoltDB{db: db}, nil
  52. }
  53. func (bdb *BoltDB) Get(key []byte) (value []byte) {
  54. key = nonEmptyKey(nonNilBytes(key))
  55. err := bdb.db.View(func(tx *bbolt.Tx) error {
  56. b := tx.Bucket(bucket)
  57. if v := b.Get(key); v != nil {
  58. value = append([]byte{}, v...)
  59. }
  60. return nil
  61. })
  62. if err != nil {
  63. panic(err)
  64. }
  65. return
  66. }
  67. func (bdb *BoltDB) Has(key []byte) bool {
  68. return bdb.Get(key) != nil
  69. }
  70. func (bdb *BoltDB) Set(key, value []byte) {
  71. key = nonEmptyKey(nonNilBytes(key))
  72. value = nonNilBytes(value)
  73. err := bdb.db.Update(func(tx *bbolt.Tx) error {
  74. b := tx.Bucket(bucket)
  75. return b.Put(key, value)
  76. })
  77. if err != nil {
  78. panic(err)
  79. }
  80. }
  81. func (bdb *BoltDB) SetSync(key, value []byte) {
  82. bdb.Set(key, value)
  83. }
  84. func (bdb *BoltDB) Delete(key []byte) {
  85. key = nonEmptyKey(nonNilBytes(key))
  86. err := bdb.db.Update(func(tx *bbolt.Tx) error {
  87. return tx.Bucket(bucket).Delete(key)
  88. })
  89. if err != nil {
  90. panic(err)
  91. }
  92. }
  93. func (bdb *BoltDB) DeleteSync(key []byte) {
  94. bdb.Delete(key)
  95. }
  96. func (bdb *BoltDB) Close() {
  97. bdb.db.Close()
  98. }
  99. func (bdb *BoltDB) Print() {
  100. stats := bdb.db.Stats()
  101. fmt.Printf("%v\n", stats)
  102. err := bdb.db.View(func(tx *bbolt.Tx) error {
  103. tx.Bucket(bucket).ForEach(func(k, v []byte) error {
  104. fmt.Printf("[%X]:\t[%X]\n", k, v)
  105. return nil
  106. })
  107. return nil
  108. })
  109. if err != nil {
  110. panic(err)
  111. }
  112. }
  113. func (bdb *BoltDB) Stats() map[string]string {
  114. stats := bdb.db.Stats()
  115. m := make(map[string]string)
  116. // Freelist stats
  117. m["FreePageN"] = fmt.Sprintf("%v", stats.FreePageN)
  118. m["PendingPageN"] = fmt.Sprintf("%v", stats.PendingPageN)
  119. m["FreeAlloc"] = fmt.Sprintf("%v", stats.FreeAlloc)
  120. m["FreelistInuse"] = fmt.Sprintf("%v", stats.FreelistInuse)
  121. // Transaction stats
  122. m["TxN"] = fmt.Sprintf("%v", stats.TxN)
  123. m["OpenTxN"] = fmt.Sprintf("%v", stats.OpenTxN)
  124. return m
  125. }
  126. // boltDBBatch stores key values in sync.Map and dumps them to the underlying
  127. // DB upon Write call.
  128. type boltDBBatch struct {
  129. db *BoltDB
  130. ops []operation
  131. }
  132. // NewBatch returns a new batch.
  133. func (bdb *BoltDB) NewBatch() Batch {
  134. return &boltDBBatch{
  135. ops: nil,
  136. db: bdb,
  137. }
  138. }
  139. // It is safe to modify the contents of the argument after Set returns but not
  140. // before.
  141. func (bdb *boltDBBatch) Set(key, value []byte) {
  142. bdb.ops = append(bdb.ops, operation{opTypeSet, key, value})
  143. }
  144. // It is safe to modify the contents of the argument after Delete returns but
  145. // not before.
  146. func (bdb *boltDBBatch) Delete(key []byte) {
  147. bdb.ops = append(bdb.ops, operation{opTypeDelete, key, nil})
  148. }
  149. // NOTE: the operation is synchronous (see BoltDB for reasons)
  150. func (bdb *boltDBBatch) Write() {
  151. err := bdb.db.db.Batch(func(tx *bbolt.Tx) error {
  152. b := tx.Bucket(bucket)
  153. for _, op := range bdb.ops {
  154. key := nonEmptyKey(nonNilBytes(op.key))
  155. switch op.opType {
  156. case opTypeSet:
  157. if putErr := b.Put(key, op.value); putErr != nil {
  158. return putErr
  159. }
  160. case opTypeDelete:
  161. if delErr := b.Delete(key); delErr != nil {
  162. return delErr
  163. }
  164. }
  165. }
  166. return nil
  167. })
  168. if err != nil {
  169. panic(err)
  170. }
  171. }
  172. func (bdb *boltDBBatch) WriteSync() {
  173. bdb.Write()
  174. }
  175. func (bdb *boltDBBatch) Close() {}
  176. // WARNING: Any concurrent writes or reads will block until the iterator is
  177. // closed.
  178. func (bdb *BoltDB) Iterator(start, end []byte) Iterator {
  179. tx, err := bdb.db.Begin(false)
  180. if err != nil {
  181. panic(err)
  182. }
  183. return newBoltDBIterator(tx, start, end, false)
  184. }
  185. // WARNING: Any concurrent writes or reads will block until the iterator is
  186. // closed.
  187. func (bdb *BoltDB) ReverseIterator(start, end []byte) Iterator {
  188. tx, err := bdb.db.Begin(false)
  189. if err != nil {
  190. panic(err)
  191. }
  192. return newBoltDBIterator(tx, start, end, true)
  193. }
  194. // boltDBIterator allows you to iterate on range of keys/values given some
  195. // start / end keys (nil & nil will result in doing full scan).
  196. type boltDBIterator struct {
  197. tx *bbolt.Tx
  198. itr *bbolt.Cursor
  199. start []byte
  200. end []byte
  201. currentKey []byte
  202. currentValue []byte
  203. isInvalid bool
  204. isReverse bool
  205. }
  206. func newBoltDBIterator(tx *bbolt.Tx, start, end []byte, isReverse bool) *boltDBIterator {
  207. itr := tx.Bucket(bucket).Cursor()
  208. var ck, cv []byte
  209. if isReverse {
  210. if end == nil {
  211. ck, cv = itr.Last()
  212. } else {
  213. _, _ = itr.Seek(end) // after key
  214. ck, cv = itr.Prev() // return to end key
  215. }
  216. } else {
  217. if start == nil {
  218. ck, cv = itr.First()
  219. } else {
  220. ck, cv = itr.Seek(start)
  221. }
  222. }
  223. return &boltDBIterator{
  224. tx: tx,
  225. itr: itr,
  226. start: start,
  227. end: end,
  228. currentKey: ck,
  229. currentValue: cv,
  230. isReverse: isReverse,
  231. isInvalid: false,
  232. }
  233. }
  234. func (itr *boltDBIterator) Domain() ([]byte, []byte) {
  235. return itr.start, itr.end
  236. }
  237. func (itr *boltDBIterator) Valid() bool {
  238. if itr.isInvalid {
  239. return false
  240. }
  241. // iterated to the end of the cursor
  242. if len(itr.currentKey) == 0 {
  243. itr.isInvalid = true
  244. return false
  245. }
  246. if itr.isReverse {
  247. if itr.start != nil && bytes.Compare(itr.currentKey, itr.start) < 0 {
  248. itr.isInvalid = true
  249. return false
  250. }
  251. } else {
  252. if itr.end != nil && bytes.Compare(itr.end, itr.currentKey) <= 0 {
  253. itr.isInvalid = true
  254. return false
  255. }
  256. }
  257. // Valid
  258. return true
  259. }
  260. func (itr *boltDBIterator) Next() {
  261. itr.assertIsValid()
  262. if itr.isReverse {
  263. itr.currentKey, itr.currentValue = itr.itr.Prev()
  264. } else {
  265. itr.currentKey, itr.currentValue = itr.itr.Next()
  266. }
  267. }
  268. func (itr *boltDBIterator) Key() []byte {
  269. itr.assertIsValid()
  270. return append([]byte{}, itr.currentKey...)
  271. }
  272. func (itr *boltDBIterator) Value() []byte {
  273. itr.assertIsValid()
  274. var value []byte
  275. if itr.currentValue != nil {
  276. value = append([]byte{}, itr.currentValue...)
  277. }
  278. return value
  279. }
  280. func (itr *boltDBIterator) Close() {
  281. err := itr.tx.Rollback()
  282. if err != nil {
  283. panic(err)
  284. }
  285. }
  286. func (itr *boltDBIterator) assertIsValid() {
  287. if !itr.Valid() {
  288. panic("Boltdb-iterator is invalid")
  289. }
  290. }
  291. // nonEmptyKey returns a []byte("nil") if key is empty.
  292. // WARNING: this may collude with "nil" user key!
  293. func nonEmptyKey(key []byte) []byte {
  294. if len(key) == 0 {
  295. return []byte("nil")
  296. }
  297. return key
  298. }