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.

156 lines
4.4 KiB

6 years ago
6 years ago
7 years ago
6 years ago
6 years ago
  1. package storage
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "github.com/syndtr/goleveldb/leveldb/errors"
  7. "github.com/syndtr/goleveldb/leveldb/opt"
  8. "github.com/chrislusf/seaweedfs/weed/storage/idx"
  9. "github.com/syndtr/goleveldb/leveldb"
  10. "github.com/chrislusf/seaweedfs/weed/glog"
  11. "github.com/chrislusf/seaweedfs/weed/storage/needle_map"
  12. . "github.com/chrislusf/seaweedfs/weed/storage/types"
  13. "github.com/chrislusf/seaweedfs/weed/util"
  14. )
  15. type LevelDbNeedleMap struct {
  16. baseNeedleMapper
  17. dbFileName string
  18. db *leveldb.DB
  19. }
  20. func NewLevelDbNeedleMap(dbFileName string, indexFile *os.File, opts *opt.Options) (m *LevelDbNeedleMap, err error) {
  21. m = &LevelDbNeedleMap{dbFileName: dbFileName}
  22. m.indexFile = indexFile
  23. if !isLevelDbFresh(dbFileName, indexFile) {
  24. glog.V(0).Infof("Start to Generate %s from %s", dbFileName, indexFile.Name())
  25. generateLevelDbFile(dbFileName, indexFile)
  26. glog.V(0).Infof("Finished Generating %s from %s", dbFileName, indexFile.Name())
  27. }
  28. glog.V(1).Infof("Opening %s...", dbFileName)
  29. if m.db, err = leveldb.OpenFile(dbFileName, opts); err != nil {
  30. if errors.IsCorrupted(err) {
  31. m.db, err = leveldb.RecoverFile(dbFileName, opts)
  32. }
  33. if err != nil {
  34. return
  35. }
  36. }
  37. glog.V(1).Infof("Loading %s...", indexFile.Name())
  38. mm, indexLoadError := newNeedleMapMetricFromIndexFile(indexFile)
  39. if indexLoadError != nil {
  40. return nil, indexLoadError
  41. }
  42. m.mapMetric = *mm
  43. return
  44. }
  45. func isLevelDbFresh(dbFileName string, indexFile *os.File) bool {
  46. // normally we always write to index file first
  47. dbLogFile, err := os.Open(filepath.Join(dbFileName, "LOG"))
  48. if err != nil {
  49. return false
  50. }
  51. defer dbLogFile.Close()
  52. dbStat, dbStatErr := dbLogFile.Stat()
  53. indexStat, indexStatErr := indexFile.Stat()
  54. if dbStatErr != nil || indexStatErr != nil {
  55. glog.V(0).Infof("Can not stat file: %v and %v", dbStatErr, indexStatErr)
  56. return false
  57. }
  58. return dbStat.ModTime().After(indexStat.ModTime())
  59. }
  60. func generateLevelDbFile(dbFileName string, indexFile *os.File) error {
  61. db, err := leveldb.OpenFile(dbFileName, nil)
  62. if err != nil {
  63. return err
  64. }
  65. defer db.Close()
  66. return idx.WalkIndexFile(indexFile, func(key NeedleId, offset Offset, size uint32) error {
  67. if !offset.IsZero() && size != TombstoneFileSize {
  68. levelDbWrite(db, key, offset, size)
  69. } else {
  70. levelDbDelete(db, key)
  71. }
  72. return nil
  73. })
  74. }
  75. func (m *LevelDbNeedleMap) Get(key NeedleId) (element *needle_map.NeedleValue, ok bool) {
  76. bytes := make([]byte, NeedleIdSize)
  77. NeedleIdToBytes(bytes[0:NeedleIdSize], key)
  78. data, err := m.db.Get(bytes, nil)
  79. if err != nil || len(data) != OffsetSize+SizeSize {
  80. return nil, false
  81. }
  82. offset := BytesToOffset(data[0:OffsetSize])
  83. size := util.BytesToUint32(data[OffsetSize : OffsetSize+SizeSize])
  84. return &needle_map.NeedleValue{Key: key, Offset: offset, Size: size}, true
  85. }
  86. func (m *LevelDbNeedleMap) Put(key NeedleId, offset Offset, size uint32) error {
  87. var oldSize uint32
  88. if oldNeedle, ok := m.Get(key); ok {
  89. oldSize = oldNeedle.Size
  90. }
  91. m.logPut(key, oldSize, size)
  92. // write to index file first
  93. if err := m.appendToIndexFile(key, offset, size); err != nil {
  94. return fmt.Errorf("cannot write to indexfile %s: %v", m.indexFile.Name(), err)
  95. }
  96. return levelDbWrite(m.db, key, offset, size)
  97. }
  98. func levelDbWrite(db *leveldb.DB, key NeedleId, offset Offset, size uint32) error {
  99. bytes := needle_map.ToBytes(key, offset, size)
  100. if err := db.Put(bytes[0:NeedleIdSize], bytes[NeedleIdSize:NeedleIdSize+OffsetSize+SizeSize], nil); err != nil {
  101. return fmt.Errorf("failed to write leveldb: %v", err)
  102. }
  103. return nil
  104. }
  105. func levelDbDelete(db *leveldb.DB, key NeedleId) error {
  106. bytes := make([]byte, NeedleIdSize)
  107. NeedleIdToBytes(bytes, key)
  108. return db.Delete(bytes, nil)
  109. }
  110. func (m *LevelDbNeedleMap) Delete(key NeedleId, offset Offset) error {
  111. if oldNeedle, ok := m.Get(key); ok {
  112. m.logDelete(oldNeedle.Size)
  113. }
  114. // write to index file first
  115. if err := m.appendToIndexFile(key, offset, TombstoneFileSize); err != nil {
  116. return err
  117. }
  118. return levelDbDelete(m.db, key)
  119. }
  120. func (m *LevelDbNeedleMap) Close() {
  121. indexFileName := m.indexFile.Name()
  122. if err := m.indexFile.Sync(); err != nil {
  123. glog.Warningf("sync file %s failed: %v", indexFileName, err)
  124. }
  125. if err := m.indexFile.Close(); err != nil {
  126. glog.Warningf("close index file %s failed: %v", indexFileName, err)
  127. }
  128. if err := m.db.Close(); err != nil {
  129. glog.Warningf("close levelDB failed: %v", err)
  130. }
  131. }
  132. func (m *LevelDbNeedleMap) Destroy() error {
  133. m.Close()
  134. os.Remove(m.indexFile.Name())
  135. return os.RemoveAll(m.dbFileName)
  136. }