diskgraphstorage.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /*
  2. * EliasDB
  3. *
  4. * Copyright 2016 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the Mozilla Public
  7. * License, v. 2.0. If a copy of the MPL was not distributed with this
  8. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  9. */
  10. /*
  11. Package graphstorage contains classes which model storage objects for graph data.
  12. There are two main storage objects: DiskGraphStorage which provides disk storage
  13. and MemoryGraphStorage which provides memory-only storage.
  14. */
  15. package graphstorage
  16. import (
  17. "fmt"
  18. "os"
  19. "strings"
  20. "devt.de/krotik/common/datautil"
  21. "devt.de/krotik/common/fileutil"
  22. "devt.de/krotik/eliasdb/graph/util"
  23. "devt.de/krotik/eliasdb/storage"
  24. )
  25. /*
  26. FilenameNameDB is the filename for the name storage file
  27. */
  28. var FilenameNameDB = "names.pm"
  29. /*
  30. DiskGraphStorage data structure
  31. */
  32. type DiskGraphStorage struct {
  33. name string // Name of the graph storage
  34. readonly bool // Flag for readonly mode
  35. mainDB *datautil.PersistentStringMap // Database storing names
  36. storagemanagers map[string]storage.Manager // Map of StorageManagers
  37. }
  38. /*
  39. NewDiskGraphStorage creates a new DiskGraphStorage instance.
  40. */
  41. func NewDiskGraphStorage(name string, readonly bool) (Storage, error) {
  42. dgs := &DiskGraphStorage{name, readonly, nil, make(map[string]storage.Manager)}
  43. // Load the graph storage if the storage directory already exists if not try to create it
  44. if res, _ := fileutil.PathExists(name); !res {
  45. if err := os.Mkdir(name, 0770); err != nil {
  46. return nil, &util.GraphError{Type: util.ErrOpening, Detail: err.Error()}
  47. }
  48. // Create the graph storage files
  49. mainDB, err := datautil.NewPersistentStringMap(name + "/" + FilenameNameDB)
  50. if err != nil {
  51. return nil, &util.GraphError{Type: util.ErrOpening, Detail: err.Error()}
  52. }
  53. dgs.mainDB = mainDB
  54. } else {
  55. // Load graph storage files
  56. mainDB, err := datautil.LoadPersistentStringMap(name + "/" + FilenameNameDB)
  57. if err != nil {
  58. return nil, &util.GraphError{Type: util.ErrOpening, Detail: err.Error()}
  59. }
  60. dgs.mainDB = mainDB
  61. }
  62. return dgs, nil
  63. }
  64. /*
  65. Name returns the name of the DiskGraphStorage instance.
  66. */
  67. func (dgs *DiskGraphStorage) Name() string {
  68. return dgs.name
  69. }
  70. /*
  71. MainDB returns the main database.
  72. */
  73. func (dgs *DiskGraphStorage) MainDB() map[string]string {
  74. return dgs.mainDB.Data
  75. }
  76. /*
  77. RollbackMain rollback the main database.
  78. */
  79. func (dgs *DiskGraphStorage) RollbackMain() error {
  80. // Fail operation when readonly
  81. if dgs.readonly {
  82. return &util.GraphError{Type: util.ErrReadOnly, Detail: "Cannot rollback main db"}
  83. }
  84. mainDB, err := datautil.LoadPersistentStringMap(dgs.name + "/" + FilenameNameDB)
  85. if err != nil {
  86. return &util.GraphError{Type: util.ErrOpening, Detail: err.Error()}
  87. }
  88. dgs.mainDB = mainDB
  89. return nil
  90. }
  91. /*
  92. FlushMain writes the main database to the storage.
  93. */
  94. func (dgs *DiskGraphStorage) FlushMain() error {
  95. // Fail operation when readonly
  96. if dgs.readonly {
  97. return &util.GraphError{Type: util.ErrReadOnly, Detail: "Cannot flush main db"}
  98. }
  99. if err := dgs.mainDB.Flush(); err != nil {
  100. return &util.GraphError{Type: util.ErrFlushing, Detail: err.Error()}
  101. }
  102. return nil
  103. }
  104. /*
  105. StorageManager gets a storage manager with a certain name. A non-existing
  106. StorageManager is created automatically if the create flag is set to true.
  107. */
  108. func (dgs *DiskGraphStorage) StorageManager(smname string, create bool) storage.Manager {
  109. sm, ok := dgs.storagemanagers[smname]
  110. filename := dgs.name + "/" + smname
  111. // Create storage manager object either if we may create or if the
  112. // database already exists
  113. if !ok && (create || storage.DataFileExist(filename)) {
  114. dsm := storage.NewDiskStorageManager(dgs.name+"/"+smname, dgs.readonly, false, false, false)
  115. sm = storage.NewCachedDiskStorageManager(dsm, 100000)
  116. dgs.storagemanagers[smname] = sm
  117. }
  118. return sm
  119. }
  120. /*
  121. FlushAll writes all pending changes to the storage.
  122. */
  123. func (dgs *DiskGraphStorage) FlushAll() error {
  124. if dgs.readonly {
  125. return nil
  126. }
  127. var errors []string
  128. err := dgs.mainDB.Flush()
  129. if err != nil {
  130. errors = append(errors, err.Error())
  131. }
  132. for _, sm := range dgs.storagemanagers {
  133. err := sm.Flush()
  134. if err != nil {
  135. errors = append(errors, err.Error())
  136. }
  137. }
  138. if len(errors) > 0 {
  139. details := fmt.Sprint(dgs.name, " :", strings.Join(errors, "; "))
  140. return &util.GraphError{Type: util.ErrFlushing, Detail: details}
  141. }
  142. return nil
  143. }
  144. /*
  145. Close closes the storage.
  146. */
  147. func (dgs *DiskGraphStorage) Close() error {
  148. var errors []string
  149. err := dgs.mainDB.Flush()
  150. if err != nil {
  151. errors = append(errors, err.Error())
  152. }
  153. for _, sm := range dgs.storagemanagers {
  154. err := sm.Close()
  155. if err != nil {
  156. errors = append(errors, err.Error())
  157. }
  158. }
  159. if len(errors) > 0 {
  160. details := fmt.Sprint(dgs.name, " :", strings.Join(errors, "; "))
  161. return &util.GraphError{Type: util.ErrClosing, Detail: details}
  162. }
  163. return nil
  164. }