helpers.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  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. package graph
  11. import (
  12. "bytes"
  13. "encoding/binary"
  14. "encoding/gob"
  15. "fmt"
  16. "strings"
  17. "devt.de/krotik/common/stringutil"
  18. "devt.de/krotik/eliasdb/graph/data"
  19. "devt.de/krotik/eliasdb/graph/util"
  20. "devt.de/krotik/eliasdb/hash"
  21. "devt.de/krotik/eliasdb/storage"
  22. )
  23. // Helper functions for GraphManager
  24. // =================================
  25. /*
  26. checkPartitionName checks if a given partition name is valid.
  27. */
  28. func (gm *Manager) checkPartitionName(part string) error {
  29. if !stringutil.IsAlphaNumeric(part) {
  30. return &util.GraphError{
  31. Type: util.ErrInvalidData,
  32. Detail: fmt.Sprintf("Partition name %v is not alphanumeric - can only contain [a-zA-Z0-9_]", part),
  33. }
  34. }
  35. return nil
  36. }
  37. /*
  38. checkNode checks if a given node can be written to the datastore.
  39. */
  40. func (gm *Manager) checkNode(node data.Node) error {
  41. return gm.checkItemGeneral(node, "Node")
  42. }
  43. /*
  44. checkItemGeneral checks the general properties of a given graph item.
  45. */
  46. func (gm *Manager) checkItemGeneral(node data.Node, name string) error {
  47. if node.Key() == "" {
  48. return &util.GraphError{Type: util.ErrInvalidData, Detail: name + " is missing a key value"}
  49. }
  50. if node.Kind() == "" {
  51. return &util.GraphError{Type: util.ErrInvalidData, Detail: name + " is missing a kind value"}
  52. }
  53. if !stringutil.IsAlphaNumeric(node.Kind()) {
  54. return &util.GraphError{
  55. Type: util.ErrInvalidData,
  56. Detail: fmt.Sprintf("%v kind %v is not alphanumeric - can only contain [a-zA-Z0-9_]", name, node.Kind()),
  57. }
  58. }
  59. for attr := range node.Data() {
  60. if attr == "" {
  61. return &util.GraphError{Type: util.ErrInvalidData, Detail: name + " contains empty string attribute name"}
  62. }
  63. }
  64. return nil
  65. }
  66. /*
  67. checkEdge checks if a given edge can be written to the datastore.
  68. */
  69. func (gm *Manager) checkEdge(edge data.Edge) error {
  70. if err := gm.checkItemGeneral(edge, "Edge"); err != nil {
  71. return err
  72. }
  73. if edge.End1Key() == "" {
  74. return &util.GraphError{Type: util.ErrInvalidData, Detail: "Edge is missing a key value for end1"}
  75. }
  76. if edge.End1Kind() == "" {
  77. return &util.GraphError{Type: util.ErrInvalidData, Detail: "Edge is missing a kind value for end1"}
  78. }
  79. if edge.End1Role() == "" {
  80. return &util.GraphError{Type: util.ErrInvalidData, Detail: "Edge is missing a role value for end1"}
  81. } else if !stringutil.IsAlphaNumeric(edge.End1Role()) {
  82. return &util.GraphError{
  83. Type: util.ErrInvalidData,
  84. Detail: fmt.Sprintf("Edge role %v is not alphanumeric - can only contain [a-zA-Z0-9_]", edge.End1Role()),
  85. }
  86. }
  87. if _, ok := edge.Attr(data.EdgeEnd1Cascading).(bool); !ok {
  88. return &util.GraphError{Type: util.ErrInvalidData, Detail: "Edge is missing a cascading value for end1"}
  89. }
  90. if edge.End2Key() == "" {
  91. return &util.GraphError{Type: util.ErrInvalidData, Detail: "Edge is missing a key value for end2"}
  92. }
  93. if edge.End2Kind() == "" {
  94. return &util.GraphError{Type: util.ErrInvalidData, Detail: "Edge is missing a kind value for end2"}
  95. }
  96. if edge.End2Role() == "" {
  97. return &util.GraphError{Type: util.ErrInvalidData, Detail: "Edge is missing a role value for end2"}
  98. } else if !stringutil.IsAlphaNumeric(edge.End2Role()) {
  99. return &util.GraphError{
  100. Type: util.ErrInvalidData,
  101. Detail: fmt.Sprintf("Edge role %v is not alphanumeric - can only contain [a-zA-Z0-9_]", edge.End2Role()),
  102. }
  103. }
  104. if _, ok := edge.Attr(data.EdgeEnd2Cascading).(bool); !ok {
  105. return &util.GraphError{Type: util.ErrInvalidData, Detail: "Edge is missing a cascading value for end2"}
  106. }
  107. return nil
  108. }
  109. /*
  110. writeNodeCount writes a new node count for a specific kind to the datastore.
  111. */
  112. func (gm *Manager) writeNodeCount(kind string, count uint64, flush bool) error {
  113. numstr := make([]byte, 8)
  114. binary.LittleEndian.PutUint64(numstr, count)
  115. gm.gs.MainDB()[MainDBNodeCount+kind] = string(numstr)
  116. if flush {
  117. return gm.gs.FlushMain()
  118. }
  119. return nil
  120. }
  121. /*
  122. writeEdgeCount writes a new edge count for a specific kind to the datastore.
  123. */
  124. func (gm *Manager) writeEdgeCount(kind string, count uint64, flush bool) error {
  125. numstr := make([]byte, 8)
  126. binary.LittleEndian.PutUint64(numstr, count)
  127. gm.gs.MainDB()[MainDBEdgeCount+kind] = string(numstr)
  128. if flush {
  129. return gm.gs.FlushMain()
  130. }
  131. return nil
  132. }
  133. /*
  134. getNodeStorageHTree gets two HTree instances which can be used to store nodes.
  135. This function ensures that depending entries in other datastructures do exist.
  136. */
  137. func (gm *Manager) getNodeStorageHTree(part string, kind string,
  138. create bool) (*hash.HTree, *hash.HTree, error) {
  139. gm.storageMutex.Lock()
  140. defer gm.storageMutex.Unlock()
  141. // Check if the partition name is valid
  142. if err := gm.checkPartitionName(part); err != nil {
  143. return nil, nil, err
  144. }
  145. // Check if the node kind is valid
  146. if !stringutil.IsAlphaNumeric(kind) {
  147. return nil, nil, &util.GraphError{
  148. Type: util.ErrInvalidData,
  149. Detail: fmt.Sprintf("Node kind %v is not alphanumeric - can only contain [a-zA-Z0-9_]", kind),
  150. }
  151. }
  152. // Make sure all required lookup maps are there
  153. if gm.getMainDBMap(MainDBNodeKinds) == nil {
  154. gm.storeMainDBMap(MainDBNodeKinds, make(map[string]string))
  155. }
  156. if gm.getMainDBMap(MainDBParts) == nil {
  157. gm.storeMainDBMap(MainDBParts, make(map[string]string))
  158. }
  159. if gm.getMainDBMap(MainDBNodeAttrs+kind) == nil {
  160. gm.storeMainDBMap(MainDBNodeAttrs+kind, make(map[string]string))
  161. }
  162. if gm.getMainDBMap(MainDBNodeEdges+kind) == nil {
  163. gm.storeMainDBMap(MainDBNodeEdges+kind, make(map[string]string))
  164. }
  165. if _, ok := gm.gs.MainDB()[MainDBNodeCount+kind]; !ok {
  166. gm.gs.MainDB()[MainDBNodeCount+kind] = string(make([]byte, 8, 8))
  167. }
  168. // Return the actual storage
  169. gs := gm.gs.StorageManager(part+kind+StorageSuffixNodes, create)
  170. if gs == nil {
  171. return nil, nil, nil
  172. }
  173. attrTree, err := gm.getHTree(gs, RootIDNodeHTree)
  174. if err != nil {
  175. return nil, nil, err
  176. }
  177. valTree, err := gm.getHTree(gs, RootIDNodeHTreeSecond)
  178. if err != nil {
  179. return nil, nil, err
  180. }
  181. return attrTree, valTree, nil
  182. }
  183. /*
  184. getEdgeStorageHTree gets a HTree which can be used to store edges. This function ensures that depending
  185. entries in other datastructures do exist.
  186. */
  187. func (gm *Manager) getEdgeStorageHTree(part string, kind string, create bool) (*hash.HTree, error) {
  188. gm.storageMutex.Lock()
  189. defer gm.storageMutex.Unlock()
  190. // Check if the partition name is valid
  191. if err := gm.checkPartitionName(part); err != nil {
  192. return nil, err
  193. }
  194. // Check if the edge kind is valid
  195. if !stringutil.IsAlphaNumeric(kind) {
  196. return nil, &util.GraphError{
  197. Type: util.ErrInvalidData,
  198. Detail: fmt.Sprintf("Edge kind %v is not alphanumeric - can only contain [a-zA-Z0-9_]", kind),
  199. }
  200. }
  201. // Make sure all required lookup maps are there
  202. if gm.getMainDBMap(MainDBEdgeKinds) == nil {
  203. gm.storeMainDBMap(MainDBEdgeKinds, make(map[string]string))
  204. }
  205. if gm.getMainDBMap(MainDBEdgeAttrs+kind) == nil {
  206. gm.storeMainDBMap(MainDBEdgeAttrs+kind, make(map[string]string))
  207. }
  208. if _, ok := gm.gs.MainDB()[MainDBEdgeCount+kind]; !ok {
  209. gm.gs.MainDB()[MainDBEdgeCount+kind] = string(make([]byte, 8, 8))
  210. }
  211. // Return the actual storage
  212. gs := gm.gs.StorageManager(part+kind+StorageSuffixEdges, create)
  213. if gs == nil {
  214. return nil, nil
  215. }
  216. return gm.getHTree(gs, RootIDNodeHTree)
  217. }
  218. /*
  219. getNodeIndexHTree gets a HTree which can be used to index nodes.
  220. */
  221. func (gm *Manager) getNodeIndexHTree(part string, kind string, create bool) (*hash.HTree, error) {
  222. return gm.getIndexHTree(part, kind, create, "Node", StorageSuffixNodesIndex)
  223. }
  224. /*
  225. getEdgeIndexHTree gets a HTree which can be used to index edges.
  226. */
  227. func (gm *Manager) getEdgeIndexHTree(part string, kind string, create bool) (*hash.HTree, error) {
  228. return gm.getIndexHTree(part, kind, create, "Edge", StorageSuffixEdgesIndex)
  229. }
  230. /*
  231. getIndexHTree gets a HTree which can be used to index items.
  232. */
  233. func (gm *Manager) getIndexHTree(part string, kind string, create bool, name string, suffix string) (*hash.HTree, error) {
  234. gm.storageMutex.Lock()
  235. defer gm.storageMutex.Unlock()
  236. // Check if the partition name is valid
  237. if err := gm.checkPartitionName(part); err != nil {
  238. return nil, err
  239. }
  240. // Check if the kind is valid
  241. if !stringutil.IsAlphaNumeric(kind) {
  242. return nil, &util.GraphError{
  243. Type: util.ErrInvalidData,
  244. Detail: fmt.Sprintf("%v kind %v is not alphanumeric - can only contain [a-zA-Z0-9_]", name, kind),
  245. }
  246. }
  247. gs := gm.gs.StorageManager(part+kind+suffix, create)
  248. if gs == nil {
  249. return nil, nil
  250. }
  251. return gm.getHTree(gs, RootIDNodeHTree)
  252. }
  253. /*
  254. flushNodeStorage flushes a node storage.
  255. */
  256. func (gm *Manager) flushNodeStorage(part string, kind string) error {
  257. if sm := gm.gs.StorageManager(part+kind+StorageSuffixNodes, false); sm != nil {
  258. if err := sm.Flush(); err != nil {
  259. return &util.GraphError{Type: util.ErrFlushing, Detail: err.Error()}
  260. }
  261. }
  262. return nil
  263. }
  264. /*
  265. flushNodeIndex flushes a node index.
  266. */
  267. func (gm *Manager) flushNodeIndex(part string, kind string) error {
  268. if sm := gm.gs.StorageManager(part+kind+StorageSuffixNodesIndex, false); sm != nil {
  269. if err := sm.Flush(); err != nil {
  270. return &util.GraphError{Type: util.ErrFlushing, Detail: err.Error()}
  271. }
  272. }
  273. return nil
  274. }
  275. /*
  276. flushEdgeStorage flushes an edge storage.
  277. */
  278. func (gm *Manager) flushEdgeStorage(part string, kind string) error {
  279. if sm := gm.gs.StorageManager(part+kind+StorageSuffixEdges, false); sm != nil {
  280. if err := sm.Flush(); err != nil {
  281. return &util.GraphError{Type: util.ErrFlushing, Detail: err.Error()}
  282. }
  283. }
  284. return nil
  285. }
  286. /*
  287. flushEdgeIndex flushes an edge index.
  288. */
  289. func (gm *Manager) flushEdgeIndex(part string, kind string) error {
  290. if sm := gm.gs.StorageManager(part+kind+StorageSuffixEdgesIndex, false); sm != nil {
  291. if err := sm.Flush(); err != nil {
  292. return &util.GraphError{Type: util.ErrFlushing, Detail: err.Error()}
  293. }
  294. }
  295. return nil
  296. }
  297. /*
  298. rollbackNodeStorage rollbacks a node storage.
  299. */
  300. func (gm *Manager) rollbackNodeStorage(part string, kind string) error {
  301. if sm := gm.gs.StorageManager(part+kind+StorageSuffixNodes, false); sm != nil {
  302. if err := sm.Rollback(); err != nil {
  303. return &util.GraphError{Type: util.ErrRollback, Detail: err.Error()}
  304. }
  305. }
  306. return nil
  307. }
  308. /*
  309. rollbackNodeIndex rollbacks a node index.
  310. */
  311. func (gm *Manager) rollbackNodeIndex(part string, kind string) error {
  312. if sm := gm.gs.StorageManager(part+kind+StorageSuffixNodesIndex, false); sm != nil {
  313. if err := sm.Rollback(); err != nil {
  314. return &util.GraphError{Type: util.ErrRollback, Detail: err.Error()}
  315. }
  316. }
  317. return nil
  318. }
  319. /*
  320. rollbackEdgeStorage rollbacks an edge storage.
  321. */
  322. func (gm *Manager) rollbackEdgeStorage(part string, kind string) error {
  323. if sm := gm.gs.StorageManager(part+kind+StorageSuffixEdges, false); sm != nil {
  324. if err := sm.Rollback(); err != nil {
  325. return &util.GraphError{Type: util.ErrRollback, Detail: err.Error()}
  326. }
  327. }
  328. return nil
  329. }
  330. /*
  331. rollbackEdgeIndex rollbacks an edge index.
  332. */
  333. func (gm *Manager) rollbackEdgeIndex(part string, kind string) error {
  334. if sm := gm.gs.StorageManager(part+kind+StorageSuffixEdgesIndex, false); sm != nil {
  335. if err := sm.Rollback(); err != nil {
  336. return &util.GraphError{Type: util.ErrRollback, Detail: err.Error()}
  337. }
  338. }
  339. return nil
  340. }
  341. /*
  342. getHTree creates or loads a HTree from a given StorageManager. HTrees are not cached
  343. since the creation shouldn't have too much overhead.
  344. */
  345. func (gm *Manager) getHTree(sm storage.Manager, slot int) (*hash.HTree, error) {
  346. var htree *hash.HTree
  347. var err error
  348. loc := sm.Root(slot)
  349. if loc == 0 {
  350. // Create a new HTree and store its location
  351. htree, err = hash.NewHTree(sm)
  352. if err != nil {
  353. err = &util.GraphError{Type: util.ErrAccessComponent, Detail: err.Error()}
  354. } else {
  355. sm.SetRoot(slot, htree.Location())
  356. }
  357. } else {
  358. // Load existing HTree
  359. htree, err = hash.LoadHTree(sm, loc)
  360. if err != nil {
  361. err = &util.GraphError{Type: util.ErrAccessComponent, Detail: err.Error()}
  362. }
  363. }
  364. return htree, err
  365. }
  366. /*
  367. getMainDBMap gets a map from the main database.
  368. */
  369. func (gm *Manager) getMainDBMap(key string) map[string]string {
  370. // First try to cache
  371. mapval, ok := gm.mapCache[key]
  372. if ok {
  373. return mapval
  374. }
  375. // Lookup map and decode it
  376. val, ok := gm.gs.MainDB()[key]
  377. if ok {
  378. mapval = stringToMap(val)
  379. gm.mapCache[key] = mapval
  380. }
  381. return mapval
  382. }
  383. /*
  384. storeMainDBMap stores a map in the main database. The map is stored as a gob byte slice.
  385. Once it has been decoded it is cached for read operations.
  386. */
  387. func (gm *Manager) storeMainDBMap(key string, mapval map[string]string) {
  388. gm.mapCache[key] = mapval
  389. gm.gs.MainDB()[key] = mapToString(mapval)
  390. }
  391. // Static helper functions
  392. // =======================
  393. /*
  394. IsFullSpec is a function to determine if a given spec is a fully specified spec
  395. (i.e. all spec components are specified)
  396. */
  397. func IsFullSpec(spec string) bool {
  398. sspec := strings.Split(spec, ":")
  399. if len(sspec) != 4 || sspec[0] == "" || sspec[1] == "" || sspec[2] == "" || sspec[3] == "" {
  400. return false
  401. }
  402. return true
  403. }
  404. /*
  405. mapToString turns a map of strings into a single string.
  406. */
  407. func mapToString(stringmap map[string]string) string {
  408. bb := &bytes.Buffer{}
  409. gob.NewEncoder(bb).Encode(stringmap)
  410. return string(bb.Bytes())
  411. }
  412. /*
  413. stringToMap turns a string into a map of strings.
  414. */
  415. func stringToMap(mapString string) map[string]string {
  416. var stringmap map[string]string
  417. if err := gob.NewDecoder(bytes.NewBufferString(mapString)).Decode(&stringmap); err != nil {
  418. panic(fmt.Sprint("Cannot decode:", mapString, err))
  419. }
  420. return stringmap
  421. }