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.

209 lines
6.0 KiB

  1. // Copyright 2017-2018 DERO Project. All rights reserved.
  2. // Use of this source code in any form is governed by RESEARCH license.
  3. // license can be found in the LICENSE file.
  4. // GPG: 0F39 E425 8C65 3947 702A 8234 08B2 0360 A03A 9DE8
  5. //
  6. //
  7. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
  8. // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  9. // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
  10. // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  11. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  12. // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  13. // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  14. // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  15. // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16. // 32 bit and other systems will use this
  17. // +build !amd64 goleveldb
  18. package storage
  19. import "os"
  20. import "fmt"
  21. import "sync"
  22. import "path/filepath"
  23. import "encoding/binary"
  24. import "github.com/romana/rlog"
  25. import "github.com/syndtr/goleveldb/leveldb"
  26. import "github.com/syndtr/goleveldb/leveldb/opt"
  27. import log "github.com/sirupsen/logrus"
  28. import "github.com/arnaucode/derosuite/globals"
  29. type BoltStore struct {
  30. DB *leveldb.DB
  31. tx *leveldb.Transaction
  32. sync.Mutex // lock this struct
  33. }
  34. var Bolt_backend *BoltStore = &BoltStore{} // global variable
  35. var logger *log.Entry
  36. func (b *BoltStore) Init(params map[string]interface{}) (err error) {
  37. logger = globals.Logger.WithFields(log.Fields{"com": "STORE"})
  38. current_path := filepath.Join(os.TempDir(), "derod_leveldb_database.db")
  39. logger.Infof("Initializing leveldb store at path %s", current_path)
  40. // Open the my.db data file in your current directory.
  41. // It will be created if it doesn't exist.
  42. options := opt.Options{
  43. CompactionL0Trigger: 32, // default value is 4
  44. CompactionTableSize: 64 * 1024 * 1024, // default is 2 Mib
  45. WriteBuffer: 16 * 1024 * 1024, // default is 4 Mib
  46. }
  47. b.DB, err = leveldb.OpenFile(current_path, &options)
  48. if err != nil {
  49. logger.Fatalf("Cannot open boltdb store err %s\n", err)
  50. }
  51. return nil
  52. }
  53. func (b *BoltStore) Shutdown() (err error) {
  54. logger.Infof("Shutting boltdb store")
  55. if b.DB != nil {
  56. b.DB.Close()
  57. }
  58. return nil
  59. }
  60. // get a new writable tx,
  61. // we will manage the writable txs manually
  62. // since a block may cause changes to a number of fields which must be reflected atomically
  63. // this function is always triggered while the atomic lock is taken
  64. // this is done avoid a race condition in returning the tx and using it
  65. func (b *BoltStore) get_new_writable_tx() (tx *leveldb.Transaction) {
  66. if b.tx != nil {
  67. tx = b.tx // use existing pending tx
  68. } else { // create new pending tx
  69. tx, err := b.DB.OpenTransaction() // begin a new writable tx
  70. if err != nil {
  71. logger.Warnf("Error while creating new writable tx, err %s", err)
  72. } else {
  73. b.tx = tx
  74. rlog.Tracef(1, "Beginning new writable TX")
  75. }
  76. }
  77. return b.tx
  78. }
  79. // Commit the pending transaction to disk
  80. func (b *BoltStore) Commit() {
  81. b.Lock()
  82. if b.tx != nil {
  83. rlog.Tracef(1, "Committing writable TX")
  84. err := b.tx.Commit()
  85. if err != nil {
  86. logger.Warnf("Error while commit tx, err %s", err)
  87. }
  88. b.tx = nil
  89. } else {
  90. logger.Warnf("Trying to Commit a NULL transaction, NOT possible")
  91. }
  92. b.Unlock()
  93. }
  94. // Roll back existing changes to disk
  95. func (b *BoltStore) Rollback() {
  96. b.Lock()
  97. if b.tx != nil {
  98. rlog.Tracef(1, "Rollbacking writable TX")
  99. b.tx.Discard()
  100. b.tx = nil
  101. } else {
  102. //logger.Warnf("Trying to Rollback a NULL transaction, NOT possible")
  103. }
  104. b.Unlock()
  105. }
  106. // sync the DB to disk
  107. func (b *BoltStore) Sync() {
  108. b.Lock()
  109. if b.DB != nil {
  110. //b.DB.Sync() // sync the DB
  111. }
  112. b.Unlock()
  113. }
  114. func (b *BoltStore) StoreObject(universe_name []byte, galaxy_name []byte, solar_name []byte, key []byte, data []byte) (err error) {
  115. b.Lock()
  116. defer b.Unlock()
  117. rlog.Tracef(10, "Storing object %s %s %x data len %d\n", string(universe_name), string(galaxy_name), key, len(data))
  118. // open universe bucket
  119. tx := b.get_new_writable_tx()
  120. keyname := append(universe_name, append(galaxy_name, append(solar_name, key...)...)...)
  121. // now lets update the object attribute
  122. err = tx.Put(keyname, data, nil)
  123. return err
  124. }
  125. func (b *BoltStore) LoadObject(universe []byte, bucket_name []byte, solar_bucket []byte, key []byte) (data []byte, err error) {
  126. rlog.Tracef(10, "Loading object %s %s %x\n", string(universe), string(bucket_name), key)
  127. b.Lock()
  128. defer b.Unlock()
  129. var tx *leveldb.Transaction
  130. if b.tx != nil {
  131. tx = b.tx
  132. } else {
  133. tx, err = b.DB.OpenTransaction() // create writable tx and discard it
  134. defer tx.Discard() // tx must be rolled back always, never commit
  135. }
  136. // open universe bucket
  137. {
  138. keyname := append(universe, append(bucket_name, append(solar_bucket, key...)...)...)
  139. // now lets find the object
  140. data, err = tx.Get(keyname, nil)
  141. }
  142. return
  143. }
  144. // this function stores a uint64
  145. // this will automcatically use the lock
  146. func (b *BoltStore) StoreUint64(universe_bucket []byte, galaxy_bucket []byte, solar_bucket []byte, key []byte, data uint64) error {
  147. return b.StoreObject(universe_bucket, galaxy_bucket, solar_bucket, key, itob(data))
  148. }
  149. // this function loads the data as 64 byte integer
  150. func (b *BoltStore) LoadUint64(universe_bucket []byte, galaxy_bucket []byte, solar_bucket []byte, key []byte) (uint64, error) {
  151. object_data, err := b.LoadObject(universe_bucket, galaxy_bucket, solar_bucket, key)
  152. if err != nil {
  153. return 0, err
  154. }
  155. if len(object_data) == 0 {
  156. return 0, fmt.Errorf("No value stored here, we should look more")
  157. }
  158. if len(object_data) != 8 {
  159. panic("Database corruption, invalid data ")
  160. }
  161. value := binary.BigEndian.Uint64(object_data)
  162. return value, nil
  163. }
  164. // itob returns an 8-byte big endian representation of v.
  165. func itob(v uint64) []byte {
  166. b := make([]byte, 8)
  167. binary.BigEndian.PutUint64(b, uint64(v))
  168. return b
  169. }