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.

store.go 4.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. // Copyright (c) 2014 Couchbase, Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // Package boltdb implements a store.KVStore on top of BoltDB. It supports the
  15. // following options:
  16. //
  17. // "bucket" (string): the name of BoltDB bucket to use, defaults to "bleve".
  18. //
  19. // "nosync" (bool): if true, set boltdb.DB.NoSync to true. It speeds up index
  20. // operations in exchange of losing integrity guarantees if indexation aborts
  21. // without closing the index. Use it when rebuilding indexes from zero.
  22. package boltdb
  23. import (
  24. "bytes"
  25. "encoding/json"
  26. "fmt"
  27. "os"
  28. "github.com/blevesearch/bleve/index/store"
  29. "github.com/blevesearch/bleve/registry"
  30. bolt "go.etcd.io/bbolt"
  31. )
  32. const (
  33. Name = "boltdb"
  34. defaultCompactBatchSize = 100
  35. )
  36. type Store struct {
  37. path string
  38. bucket string
  39. db *bolt.DB
  40. noSync bool
  41. fillPercent float64
  42. mo store.MergeOperator
  43. }
  44. func New(mo store.MergeOperator, config map[string]interface{}) (store.KVStore, error) {
  45. path, ok := config["path"].(string)
  46. if !ok {
  47. return nil, fmt.Errorf("must specify path")
  48. }
  49. if path == "" {
  50. return nil, os.ErrInvalid
  51. }
  52. bucket, ok := config["bucket"].(string)
  53. if !ok {
  54. bucket = "bleve"
  55. }
  56. noSync, _ := config["nosync"].(bool)
  57. fillPercent, ok := config["fillPercent"].(float64)
  58. if !ok {
  59. fillPercent = bolt.DefaultFillPercent
  60. }
  61. bo := &bolt.Options{}
  62. ro, ok := config["read_only"].(bool)
  63. if ok {
  64. bo.ReadOnly = ro
  65. }
  66. if initialMmapSize, ok := config["initialMmapSize"].(int); ok {
  67. bo.InitialMmapSize = initialMmapSize
  68. } else if initialMmapSize, ok := config["initialMmapSize"].(float64); ok {
  69. bo.InitialMmapSize = int(initialMmapSize)
  70. }
  71. db, err := bolt.Open(path, 0600, bo)
  72. if err != nil {
  73. return nil, err
  74. }
  75. db.NoSync = noSync
  76. if !bo.ReadOnly {
  77. err = db.Update(func(tx *bolt.Tx) error {
  78. _, err := tx.CreateBucketIfNotExists([]byte(bucket))
  79. return err
  80. })
  81. if err != nil {
  82. return nil, err
  83. }
  84. }
  85. rv := Store{
  86. path: path,
  87. bucket: bucket,
  88. db: db,
  89. mo: mo,
  90. noSync: noSync,
  91. fillPercent: fillPercent,
  92. }
  93. return &rv, nil
  94. }
  95. func (bs *Store) Close() error {
  96. return bs.db.Close()
  97. }
  98. func (bs *Store) Reader() (store.KVReader, error) {
  99. tx, err := bs.db.Begin(false)
  100. if err != nil {
  101. return nil, err
  102. }
  103. return &Reader{
  104. store: bs,
  105. tx: tx,
  106. bucket: tx.Bucket([]byte(bs.bucket)),
  107. }, nil
  108. }
  109. func (bs *Store) Writer() (store.KVWriter, error) {
  110. return &Writer{
  111. store: bs,
  112. }, nil
  113. }
  114. func (bs *Store) Stats() json.Marshaler {
  115. return &stats{
  116. s: bs,
  117. }
  118. }
  119. // CompactWithBatchSize removes DictionaryTerm entries with a count of zero (in batchSize batches)
  120. // Removing entries is a workaround for github issue #374.
  121. func (bs *Store) CompactWithBatchSize(batchSize int) error {
  122. for {
  123. cnt := 0
  124. err := bs.db.Batch(func(tx *bolt.Tx) error {
  125. c := tx.Bucket([]byte(bs.bucket)).Cursor()
  126. prefix := []byte("d")
  127. for k, v := c.Seek(prefix); bytes.HasPrefix(k, prefix); k, v = c.Next() {
  128. if bytes.Equal(v, []byte{0}) {
  129. cnt++
  130. if err := c.Delete(); err != nil {
  131. return err
  132. }
  133. if cnt == batchSize {
  134. break
  135. }
  136. }
  137. }
  138. return nil
  139. })
  140. if err != nil {
  141. return err
  142. }
  143. if cnt == 0 {
  144. break
  145. }
  146. }
  147. return nil
  148. }
  149. // Compact calls CompactWithBatchSize with a default batch size of 100. This is a workaround
  150. // for github issue #374.
  151. func (bs *Store) Compact() error {
  152. return bs.CompactWithBatchSize(defaultCompactBatchSize)
  153. }
  154. func init() {
  155. registry.RegisterKVStore(Name, New)
  156. }