blocktree.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. // SiYuan - Refactor your thinking
  2. // Copyright (c) 2020-present, b3log.org
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Affero General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Affero General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Affero General Public License
  15. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. package treenode
  17. import (
  18. "os"
  19. "path/filepath"
  20. "strings"
  21. "sync"
  22. "time"
  23. "github.com/88250/gulu"
  24. "github.com/88250/lute/ast"
  25. "github.com/88250/lute/parse"
  26. "github.com/dustin/go-humanize"
  27. "github.com/panjf2000/ants/v2"
  28. util2 "github.com/siyuan-note/dejavu/util"
  29. "github.com/siyuan-note/logging"
  30. "github.com/siyuan-note/siyuan/kernel/util"
  31. "github.com/vmihailenco/msgpack/v5"
  32. )
  33. var blockTrees = &sync.Map{}
  34. type btSlice struct {
  35. data map[string]*BlockTree
  36. changed time.Time
  37. m *sync.Mutex
  38. }
  39. type BlockTree struct {
  40. ID string // 块 ID
  41. RootID string // 根 ID
  42. ParentID string // 父 ID
  43. BoxID string // 笔记本 ID
  44. Path string // 文档数据路径
  45. HPath string // 文档可读路径
  46. Updated string // 更新时间
  47. Type string // 类型
  48. }
  49. func GetBlockTreeByPath(path string) (ret *BlockTree) {
  50. blockTrees.Range(func(key, value interface{}) bool {
  51. slice := value.(*btSlice)
  52. slice.m.Lock()
  53. for _, b := range slice.data {
  54. if b.Path == path {
  55. ret = b
  56. break
  57. }
  58. }
  59. slice.m.Unlock()
  60. return nil == ret
  61. })
  62. return
  63. }
  64. func CountTrees() (ret int) {
  65. roots := map[string]bool{}
  66. blockTrees.Range(func(key, value interface{}) bool {
  67. slice := value.(*btSlice)
  68. slice.m.Lock()
  69. for _, b := range slice.data {
  70. roots[b.RootID] = true
  71. }
  72. slice.m.Unlock()
  73. return true
  74. })
  75. ret = len(roots)
  76. return
  77. }
  78. func CountBlocks() (ret int) {
  79. blockTrees.Range(func(key, value interface{}) bool {
  80. slice := value.(*btSlice)
  81. slice.m.Lock()
  82. ret += len(slice.data)
  83. slice.m.Unlock()
  84. return true
  85. })
  86. return
  87. }
  88. func GetBlockTreeRootByPath(boxID, path string) (ret *BlockTree) {
  89. blockTrees.Range(func(key, value interface{}) bool {
  90. slice := value.(*btSlice)
  91. slice.m.Lock()
  92. for _, b := range slice.data {
  93. if b.BoxID == boxID && b.Path == path && b.RootID == b.ID {
  94. ret = b
  95. break
  96. }
  97. }
  98. slice.m.Unlock()
  99. return nil == ret
  100. })
  101. return
  102. }
  103. func GetBlockTreeRootByHPath(boxID, hPath string) (ret *BlockTree) {
  104. blockTrees.Range(func(key, value interface{}) bool {
  105. slice := value.(*btSlice)
  106. slice.m.Lock()
  107. for _, b := range slice.data {
  108. if b.BoxID == boxID && b.HPath == hPath && b.RootID == b.ID {
  109. ret = b
  110. break
  111. }
  112. }
  113. slice.m.Unlock()
  114. return nil == ret
  115. })
  116. return
  117. }
  118. func GetBlockTreeRootByHPathPreferredParentID(boxID, hPath, preferredParentID string) (ret *BlockTree) {
  119. var roots []*BlockTree
  120. blockTrees.Range(func(key, value interface{}) bool {
  121. slice := value.(*btSlice)
  122. slice.m.Lock()
  123. for _, b := range slice.data {
  124. if b.BoxID == boxID && b.HPath == hPath && b.RootID == b.ID {
  125. if "" == preferredParentID {
  126. ret = b
  127. break
  128. }
  129. roots = append(roots, b)
  130. }
  131. }
  132. slice.m.Unlock()
  133. return nil == ret
  134. })
  135. if 1 > len(roots) {
  136. return
  137. }
  138. for _, root := range roots {
  139. if root.ID == preferredParentID {
  140. ret = root
  141. return
  142. }
  143. }
  144. ret = roots[0]
  145. return
  146. }
  147. func GetBlockTree(id string) (ret *BlockTree) {
  148. if "" == id {
  149. return
  150. }
  151. hash := btHash(id)
  152. val, ok := blockTrees.Load(hash)
  153. if !ok {
  154. return
  155. }
  156. slice := val.(*btSlice)
  157. slice.m.Lock()
  158. ret = slice.data[id]
  159. slice.m.Unlock()
  160. return
  161. }
  162. func SetBlockTreePath(tree *parse.Tree) {
  163. RemoveBlockTreesByRootID(tree.ID)
  164. IndexBlockTree(tree)
  165. }
  166. func RemoveBlockTreesByRootID(rootID string) {
  167. var ids []string
  168. blockTrees.Range(func(key, value interface{}) bool {
  169. slice := value.(*btSlice)
  170. slice.m.Lock()
  171. for _, b := range slice.data {
  172. if b.RootID == rootID {
  173. ids = append(ids, b.ID)
  174. }
  175. }
  176. slice.m.Unlock()
  177. return true
  178. })
  179. ids = gulu.Str.RemoveDuplicatedElem(ids)
  180. for _, id := range ids {
  181. val, ok := blockTrees.Load(btHash(id))
  182. if !ok {
  183. continue
  184. }
  185. slice := val.(*btSlice)
  186. slice.m.Lock()
  187. delete(slice.data, id)
  188. slice.m.Unlock()
  189. slice.changed = time.Now()
  190. }
  191. }
  192. func GetBlockTreesByPathPrefix(pathPrefix string) (ret []*BlockTree) {
  193. blockTrees.Range(func(key, value interface{}) bool {
  194. slice := value.(*btSlice)
  195. slice.m.Lock()
  196. for _, b := range slice.data {
  197. if strings.HasPrefix(b.Path, pathPrefix) {
  198. ret = append(ret, b)
  199. }
  200. }
  201. slice.m.Unlock()
  202. return true
  203. })
  204. return
  205. }
  206. func RemoveBlockTreesByPathPrefix(pathPrefix string) {
  207. var ids []string
  208. blockTrees.Range(func(key, value interface{}) bool {
  209. slice := value.(*btSlice)
  210. slice.m.Lock()
  211. for _, b := range slice.data {
  212. if strings.HasPrefix(b.Path, pathPrefix) {
  213. ids = append(ids, b.ID)
  214. }
  215. }
  216. slice.m.Unlock()
  217. return true
  218. })
  219. ids = gulu.Str.RemoveDuplicatedElem(ids)
  220. for _, id := range ids {
  221. val, ok := blockTrees.Load(btHash(id))
  222. if !ok {
  223. continue
  224. }
  225. slice := val.(*btSlice)
  226. slice.m.Lock()
  227. delete(slice.data, id)
  228. slice.m.Unlock()
  229. slice.changed = time.Now()
  230. }
  231. }
  232. func GetBlockTreesByBoxID(boxID string) (ret []*BlockTree) {
  233. blockTrees.Range(func(key, value interface{}) bool {
  234. slice := value.(*btSlice)
  235. slice.m.Lock()
  236. for _, b := range slice.data {
  237. if b.BoxID == boxID {
  238. ret = append(ret, b)
  239. }
  240. }
  241. slice.m.Unlock()
  242. return true
  243. })
  244. return
  245. }
  246. func RemoveBlockTreesByBoxID(boxID string) (ids []string) {
  247. blockTrees.Range(func(key, value interface{}) bool {
  248. slice := value.(*btSlice)
  249. slice.m.Lock()
  250. for _, b := range slice.data {
  251. if b.BoxID == boxID {
  252. ids = append(ids, b.ID)
  253. }
  254. }
  255. slice.m.Unlock()
  256. return true
  257. })
  258. ids = gulu.Str.RemoveDuplicatedElem(ids)
  259. for _, id := range ids {
  260. val, ok := blockTrees.Load(btHash(id))
  261. if !ok {
  262. continue
  263. }
  264. slice := val.(*btSlice)
  265. slice.m.Lock()
  266. delete(slice.data, id)
  267. slice.m.Unlock()
  268. slice.changed = time.Now()
  269. }
  270. return
  271. }
  272. func RemoveBlockTree(id string) {
  273. val, ok := blockTrees.Load(btHash(id))
  274. if !ok {
  275. return
  276. }
  277. slice := val.(*btSlice)
  278. slice.m.Lock()
  279. delete(slice.data, id)
  280. slice.m.Unlock()
  281. slice.changed = time.Now()
  282. }
  283. func IndexBlockTree(tree *parse.Tree) {
  284. var changedNodes []*ast.Node
  285. ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
  286. if !entering || !n.IsBlock() {
  287. return ast.WalkContinue
  288. }
  289. if "" == n.ID {
  290. return ast.WalkContinue
  291. }
  292. hash := btHash(n.ID)
  293. val, ok := blockTrees.Load(hash)
  294. if !ok {
  295. val = &btSlice{data: map[string]*BlockTree{}, changed: time.Time{}, m: &sync.Mutex{}}
  296. blockTrees.Store(hash, val)
  297. }
  298. slice := val.(*btSlice)
  299. slice.m.Lock()
  300. bt := slice.data[n.ID]
  301. slice.m.Unlock()
  302. if nil != bt {
  303. if bt.Updated != n.IALAttr("updated") || bt.Type != TypeAbbr(n.Type.String()) || bt.Path != tree.Path || bt.BoxID != tree.Box || bt.HPath != tree.HPath {
  304. children := ChildBlockNodes(n) // 需要考虑子块,因为一些操作(比如移动块)后需要同时更新子块
  305. changedNodes = append(changedNodes, children...)
  306. }
  307. } else {
  308. children := ChildBlockNodes(n)
  309. changedNodes = append(changedNodes, children...)
  310. }
  311. return ast.WalkContinue
  312. })
  313. for _, n := range changedNodes {
  314. updateBtSlice(n, tree)
  315. }
  316. }
  317. func updateBtSlice(n *ast.Node, tree *parse.Tree) {
  318. var parentID string
  319. if nil != n.Parent {
  320. parentID = n.Parent.ID
  321. }
  322. hash := btHash(n.ID)
  323. val, ok := blockTrees.Load(hash)
  324. if !ok {
  325. val = &btSlice{data: map[string]*BlockTree{}, changed: time.Time{}, m: &sync.Mutex{}}
  326. blockTrees.Store(hash, val)
  327. }
  328. slice := val.(*btSlice)
  329. slice.m.Lock()
  330. slice.data[n.ID] = &BlockTree{ID: n.ID, ParentID: parentID, RootID: tree.ID, BoxID: tree.Box, Path: tree.Path, HPath: tree.HPath, Updated: n.IALAttr("updated"), Type: TypeAbbr(n.Type.String())}
  331. slice.changed = time.Now()
  332. slice.m.Unlock()
  333. }
  334. var blockTreeLock = sync.Mutex{}
  335. func InitBlockTree(force bool) {
  336. blockTreeLock.Lock()
  337. defer blockTreeLock.Unlock()
  338. start := time.Now()
  339. if force {
  340. err := os.RemoveAll(util.BlockTreePath)
  341. if nil != err {
  342. logging.LogErrorf("remove block tree file failed: %s", err)
  343. }
  344. blockTrees = &sync.Map{}
  345. return
  346. }
  347. entries, err := os.ReadDir(util.BlockTreePath)
  348. if nil != err {
  349. logging.LogErrorf("read block tree dir failed: %s", err)
  350. os.Exit(logging.ExitCodeFileSysErr)
  351. return
  352. }
  353. size := int64(0)
  354. waitGroup := &sync.WaitGroup{}
  355. p, _ := ants.NewPoolWithFunc(4, func(arg interface{}) {
  356. defer waitGroup.Done()
  357. entry := arg.(os.DirEntry)
  358. p := filepath.Join(util.BlockTreePath, entry.Name())
  359. f, err := os.OpenFile(p, os.O_RDONLY, 0644)
  360. if nil != err {
  361. logging.LogErrorf("open block tree failed: %s", err)
  362. os.Exit(logging.ExitCodeFileSysErr)
  363. return
  364. }
  365. info, err := f.Stat()
  366. if nil != err {
  367. logging.LogErrorf("stat block tree failed: %s", err)
  368. os.Exit(logging.ExitCodeFileSysErr)
  369. return
  370. }
  371. size += info.Size()
  372. sliceData := map[string]*BlockTree{}
  373. if err = msgpack.NewDecoder(f).Decode(&sliceData); nil != err {
  374. logging.LogErrorf("unmarshal block tree failed: %s", err)
  375. if err = os.RemoveAll(util.BlockTreePath); nil != err {
  376. logging.LogErrorf("removed corrupted block tree failed: %s", err)
  377. }
  378. os.Exit(logging.ExitCodeFileSysErr)
  379. return
  380. }
  381. if err = f.Close(); nil != err {
  382. logging.LogErrorf("close block tree failed: %s", err)
  383. os.Exit(logging.ExitCodeFileSysErr)
  384. return
  385. }
  386. name := entry.Name()[0:strings.Index(entry.Name(), ".")]
  387. blockTrees.Store(name, &btSlice{data: sliceData, changed: time.Time{}, m: &sync.Mutex{}})
  388. })
  389. for _, entry := range entries {
  390. if !strings.HasSuffix(entry.Name(), ".msgpack") {
  391. continue
  392. }
  393. waitGroup.Add(1)
  394. p.Invoke(entry)
  395. }
  396. waitGroup.Wait()
  397. p.Release()
  398. elapsed := time.Since(start).Seconds()
  399. logging.LogInfof("read block tree [%s] to [%s], elapsed [%.2fs]", humanize.Bytes(uint64(size)), util.BlockTreePath, elapsed)
  400. return
  401. }
  402. func SaveBlockTreeJob() {
  403. SaveBlockTree(false)
  404. }
  405. func SaveBlockTree(force bool) {
  406. blockTreeLock.Lock()
  407. defer blockTreeLock.Unlock()
  408. start := time.Now()
  409. if err := os.MkdirAll(util.BlockTreePath, 0755); nil != err {
  410. logging.LogErrorf("create block tree dir [%s] failed: %s", util.BlockTreePath, err)
  411. os.Exit(logging.ExitCodeFileSysErr)
  412. return
  413. }
  414. size := uint64(0)
  415. var count int
  416. blockTrees.Range(func(key, value interface{}) bool {
  417. slice := value.(*btSlice)
  418. if !force && slice.changed.IsZero() {
  419. return true
  420. }
  421. slice.m.Lock()
  422. data, err := msgpack.Marshal(slice.data)
  423. if nil != err {
  424. logging.LogErrorf("marshal block tree failed: %s", err)
  425. os.Exit(logging.ExitCodeFileSysErr)
  426. return false
  427. }
  428. slice.m.Unlock()
  429. p := filepath.Join(util.BlockTreePath, key.(string)) + ".msgpack"
  430. if err = gulu.File.WriteFileSafer(p, data, 0644); nil != err {
  431. logging.LogErrorf("write block tree failed: %s", err)
  432. os.Exit(logging.ExitCodeFileSysErr)
  433. return false
  434. }
  435. slice.changed = time.Time{}
  436. size += uint64(len(data))
  437. count++
  438. return true
  439. })
  440. if 0 < count {
  441. //logging.LogInfof("wrote block trees [%d]", count)
  442. }
  443. elapsed := time.Since(start).Seconds()
  444. if 2 < elapsed {
  445. logging.LogWarnf("save block tree [size=%s] to [%s], elapsed [%.2fs]", humanize.Bytes(size), util.BlockTreePath, elapsed)
  446. }
  447. }
  448. func CeilTreeCount(count int) int {
  449. if 100 > count {
  450. return 100
  451. }
  452. for i := 1; i < 40; i++ {
  453. if count < i*500 {
  454. return i * 500
  455. }
  456. }
  457. return 500*40 + 1
  458. }
  459. func CeilBlockCount(count int) int {
  460. if 5000 > count {
  461. return 5000
  462. }
  463. for i := 1; i < 100; i++ {
  464. if count < i*10000 {
  465. return i * 10000
  466. }
  467. }
  468. return 10000*100 + 1
  469. }
  470. func btHash(id string) string {
  471. return util2.Hash([]byte(id))[0:2]
  472. }