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.

233 lines
5.4 KiB

  1. package storage
  2. import (
  3. "code.google.com/p/weed-fs/go/util"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "github.com/tgulacsi/go-cdb"
  8. "os"
  9. "path/filepath"
  10. )
  11. // CDB-backed read-only needle map
  12. type cdbMap struct {
  13. c1, c2 *cdb.Cdb
  14. fn1, fn2 string
  15. mapMetric
  16. }
  17. const maxCdbRecCount = 100000000
  18. var errReadOnly = errors.New("cannot modify a read-only map")
  19. // opens the cdb file(s) (base.cdb OR base.1.cdb AND base.2.cdb)
  20. // in case of two files, the metric (at key 'M') must be in base.2.cdb
  21. func OpenCdbMap(fileName string) (m *cdbMap, err error) {
  22. m = new(cdbMap)
  23. if m.c1, err = cdb.Open(fileName); err == nil {
  24. m.fn1 = fileName
  25. err = getMetric(m.c1, &m.mapMetric)
  26. return
  27. }
  28. if os.IsNotExist(err) {
  29. bn, ext := baseFilename(fileName)
  30. m.fn1 = bn + ".1" + ext
  31. if m.c1, err = cdb.Open(m.fn1); err != nil {
  32. return nil, err
  33. }
  34. m.fn2 = bn + ".2" + ext
  35. if m.c2, err = cdb.Open(m.fn2); err != nil {
  36. return nil, err
  37. }
  38. err = getMetric(m.c2, &m.mapMetric)
  39. return
  40. }
  41. return nil, err
  42. }
  43. func (m *cdbMap) Put(key uint64, offset uint32, size uint32) (int, error) {
  44. return -1, errReadOnly
  45. }
  46. func (m *cdbMap) Delete(key uint64) error {
  47. return errReadOnly
  48. }
  49. func (m *cdbMap) Close() {
  50. if m.c2 != nil {
  51. m.c2.Close()
  52. m.c2 = nil
  53. }
  54. if m.c1 != nil {
  55. m.c1.Close()
  56. m.c1 = nil
  57. }
  58. }
  59. func (m cdbMap) ContentSize() uint64 {
  60. return m.FileByteCounter
  61. }
  62. func (m cdbMap) DeletedSize() uint64 {
  63. return m.DeletionByteCounter
  64. }
  65. func (m cdbMap) FileCount() int {
  66. return m.FileCounter
  67. }
  68. func (m *cdbMap) DeletedCount() int {
  69. return m.DeletionCounter
  70. }
  71. func (m *cdbMap) NextFileKey(count int) (uint64) {
  72. return 0
  73. }
  74. func getMetric(c *cdb.Cdb, m *mapMetric) error {
  75. data, err := c.Data([]byte{'M'})
  76. if err != nil {
  77. return err
  78. }
  79. return json.Unmarshal(data, m)
  80. }
  81. func (m cdbMap) Get(key uint64) (element *NeedleValue, ok bool) {
  82. var (
  83. data []byte
  84. k []byte = make([]byte, 8)
  85. err error
  86. )
  87. util.Uint64toBytes(k, key)
  88. if data, err = m.c1.Data(k); err != nil || data == nil {
  89. if m.c2 == nil {
  90. return nil, false
  91. }
  92. if data, err = m.c2.Data(k); err != nil || data == nil {
  93. return nil, false
  94. }
  95. }
  96. return &NeedleValue{Key: Key(key), Offset: util.BytesToUint32(data[:4]),
  97. Size: util.BytesToUint32(data[4:])}, true
  98. }
  99. func (m cdbMap) Visit(visit func(NeedleValue) error) (err error) {
  100. fh, err := os.Open(m.fn1)
  101. if err != nil {
  102. return fmt.Errorf("cannot open %s: %s", m.fn1, err)
  103. }
  104. defer fh.Close()
  105. walk := func(elt cdb.Element) error {
  106. if len(elt.Key) != 8 {
  107. return nil
  108. }
  109. return visit(NeedleValue{Key: Key(util.BytesToUint64(elt.Key)),
  110. Offset: util.BytesToUint32(elt.Data[:4]),
  111. Size: util.BytesToUint32(elt.Data[4:8])})
  112. }
  113. if err = cdb.DumpMap(fh, walk); err != nil {
  114. return err
  115. }
  116. if m.c2 == nil {
  117. return nil
  118. }
  119. fh.Close()
  120. if fh, err = os.Open(m.fn2); err != nil {
  121. return fmt.Errorf("cannot open %s: %s", m.fn2, err)
  122. }
  123. return cdb.DumpMap(fh, walk)
  124. }
  125. // converts an .idx index to a cdb
  126. func ConvertIndexToCdb(cdbName string, index *os.File) error {
  127. idx, err := LoadNeedleMap(index)
  128. if err != nil {
  129. return fmt.Errorf("error loading needle map %s: %s", index, err)
  130. }
  131. defer idx.Close()
  132. return DumpNeedleMapToCdb(cdbName, idx)
  133. }
  134. // dumps a NeedleMap into a cdb
  135. func DumpNeedleMapToCdb(cdbName string, nm *NeedleMap) error {
  136. tempnam := cdbName + "t"
  137. fnames := make([]string, 1, 2)
  138. adder, closer, err := openTempCdb(tempnam)
  139. if err != nil {
  140. return fmt.Errorf("error creating factory: %s", err)
  141. }
  142. fnames[0] = tempnam
  143. elt := cdb.Element{Key: make([]byte, 8), Data: make([]byte, 8)}
  144. fcount := uint64(0)
  145. walk := func(key uint64, offset, size uint32) error {
  146. if fcount >= maxCdbRecCount {
  147. if err = closer(); err != nil {
  148. return err
  149. }
  150. tempnam = cdbName + "t2"
  151. if adder, closer, err = openTempCdb(tempnam); err != nil {
  152. return fmt.Errorf("error creating second factory: %s", err)
  153. }
  154. fnames = append(fnames, tempnam)
  155. fcount = 0
  156. }
  157. util.Uint64toBytes(elt.Key, key)
  158. util.Uint32toBytes(elt.Data[:4], offset)
  159. util.Uint32toBytes(elt.Data[4:], size)
  160. fcount++
  161. return adder(elt)
  162. }
  163. // and write out the cdb from there
  164. err = nm.Visit(func(nv NeedleValue) error {
  165. return walk(uint64(nv.Key), nv.Offset, nv.Size)
  166. })
  167. if err != nil {
  168. closer()
  169. return fmt.Errorf("error walking index %s: %s", nm, err)
  170. }
  171. // store fileBytes
  172. data, e := json.Marshal(nm.mapMetric)
  173. if e != nil {
  174. return fmt.Errorf("error marshaling metric %s: %s", nm.mapMetric, e)
  175. }
  176. if err = adder(cdb.Element{Key: []byte{'M'}, Data: data}); err != nil {
  177. return err
  178. }
  179. if err = closer(); err != nil {
  180. return err
  181. }
  182. os.Remove(cdbName)
  183. if len(fnames) == 1 {
  184. return os.Rename(fnames[0], cdbName)
  185. }
  186. bn, ext := baseFilename(cdbName)
  187. if err = os.Rename(fnames[0], bn+".1"+ext); err != nil {
  188. return err
  189. }
  190. return os.Rename(fnames[1], bn+".2"+ext)
  191. }
  192. func openTempCdb(fileName string) (cdb.AdderFunc, cdb.CloserFunc, error) {
  193. fh, err := os.Create(fileName)
  194. if err != nil {
  195. return nil, nil, fmt.Errorf("cannot create cdb file %s: %s", fileName, err.Error())
  196. }
  197. adder, closer, err := cdb.MakeFactory(fh)
  198. if err != nil {
  199. fh.Close()
  200. return nil, nil, fmt.Errorf("error creating factory: %s", err.Error())
  201. }
  202. return adder, func() error {
  203. if e := closer(); e != nil {
  204. fh.Close()
  205. return e
  206. }
  207. fh.Close()
  208. return nil
  209. }, nil
  210. }
  211. // returns filename without extension, and the extension
  212. func baseFilename(fileName string) (string, string) {
  213. ext := filepath.Ext(fileName)
  214. return fileName[:len(fileName)-len(ext)], ext
  215. }