You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

cache.go 23 kB

8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
7 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957
  1. package cache
  2. import (
  3. "errors"
  4. "io"
  5. "os"
  6. "path/filepath"
  7. "sync"
  8. "syscall"
  9. "time"
  10. "github.com/inhies/go-bytesize"
  11. "github.com/samber/lo"
  12. "gitlink.org.cn/cloudream/common/pkgs/logger"
  13. "gitlink.org.cn/cloudream/common/pkgs/trie"
  14. "gitlink.org.cn/cloudream/common/utils/io2"
  15. "gitlink.org.cn/cloudream/common/utils/lo2"
  16. "gitlink.org.cn/cloudream/jcs-pub/client/internal/db"
  17. "gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader"
  18. "gitlink.org.cn/cloudream/jcs-pub/client/internal/mount/config"
  19. "gitlink.org.cn/cloudream/jcs-pub/client/internal/mount/fuse"
  20. "gitlink.org.cn/cloudream/jcs-pub/client/internal/uploader"
  21. clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types"
  22. )
  23. type CacheEntry interface {
  24. fuse.FsEntry
  25. // 在虚拟文件系统中的路径,即不包含缓存目录的路径
  26. PathComps() []string
  27. }
  28. type CacheEntryInfo struct {
  29. PathComps []string
  30. Size int64
  31. Perm os.FileMode
  32. ModTime time.Time
  33. IsDir bool
  34. // 元数据版本号
  35. MetaRevision int
  36. // 文件数据版本号
  37. DataRevision int
  38. // 引用计数
  39. RefCount int
  40. // 上次引用计数归零的时间
  41. FreeTime time.Time
  42. // 缓存等级
  43. Level CacheLevel
  44. // 缓存等级改变时间
  45. ChangeLevelTime time.Time
  46. }
  47. type Cache struct {
  48. cfg *config.Config
  49. db *db.DB
  50. uploader *uploader.Uploader
  51. downloader *downloader.Downloader
  52. lock *sync.RWMutex
  53. cacheDone chan any
  54. activeCache *trie.Trie[*CacheFile]
  55. }
  56. func NewCache(cfg *config.Config, db *db.DB, uploader *uploader.Uploader, downloader *downloader.Downloader) *Cache {
  57. return &Cache{
  58. cfg: cfg,
  59. db: db,
  60. uploader: uploader,
  61. downloader: downloader,
  62. lock: &sync.RWMutex{},
  63. cacheDone: make(chan any),
  64. activeCache: trie.NewTrie[*CacheFile](),
  65. }
  66. }
  67. func (c *Cache) Start() {
  68. go c.scanningCache()
  69. go c.scanningData()
  70. }
  71. func (c *Cache) Stop() {
  72. close(c.cacheDone)
  73. }
  74. func (c *Cache) GetCacheDataPath(comps ...string) string {
  75. comps2 := make([]string, len(comps)+1)
  76. comps2[0] = c.cfg.DataDir
  77. copy(comps2[1:], comps)
  78. return filepath.Join(comps2...)
  79. }
  80. func (c *Cache) GetCacheMetaPath(comps ...string) string {
  81. comps2 := make([]string, len(comps)+1)
  82. comps2[0] = c.cfg.MetaDir
  83. copy(comps2[1:], comps)
  84. return filepath.Join(comps2...)
  85. }
  86. func (c *Cache) Dump() CacheStatus {
  87. c.lock.RLock()
  88. defer c.lock.RUnlock()
  89. var activeFiles []CacheFileStatus
  90. c.activeCache.Iterate(func(path []string, node *trie.Node[*CacheFile], isWordNode bool) trie.VisitCtrl {
  91. if node.Value == nil {
  92. return trie.VisitContinue
  93. }
  94. info := node.Value.Info()
  95. activeFiles = append(activeFiles, CacheFileStatus{
  96. Path: filepath.Join(path...),
  97. RefCount: info.RefCount,
  98. Level: info.Level.String(),
  99. IsUploading: node.Value.state.uploading != nil,
  100. })
  101. return trie.VisitContinue
  102. })
  103. return CacheStatus{
  104. ActiveFiles: activeFiles,
  105. }
  106. }
  107. // 获取指定位置的缓存条目信息。如果路径不存在,则返回nil。
  108. func (c *Cache) Stat(pathComps []string) *CacheEntryInfo {
  109. c.lock.RLock()
  110. defer c.lock.RUnlock()
  111. node, ok := c.activeCache.WalkEnd(pathComps)
  112. if ok && node.Value != nil {
  113. info := node.Value.Info()
  114. return &info
  115. }
  116. dataPath := c.GetCacheDataPath(pathComps...)
  117. stat, err := os.Stat(dataPath)
  118. if err != nil {
  119. // TODO 日志记录
  120. return nil
  121. }
  122. if stat.IsDir() {
  123. info, err := loadCacheDirInfo(c, pathComps, stat)
  124. if err != nil {
  125. return nil
  126. }
  127. return info
  128. }
  129. info, err := loadCacheFileInfo(c, pathComps, stat)
  130. if err != nil {
  131. return nil
  132. }
  133. return info
  134. }
  135. // 创建一个缓存文件。如果文件已经存在,则会覆盖已有文件。如果加载过程中发生了错误,或者目标位置是一个目录,则会返回nil。
  136. //
  137. // 记得使用Release减少引用计数
  138. func (c *Cache) CreateFile(pathComps []string) *CacheFile {
  139. c.lock.Lock()
  140. defer c.lock.Unlock()
  141. node, ok := c.activeCache.WalkEnd(pathComps)
  142. if ok && node.Value != nil {
  143. node.Value.Delete()
  144. if node.Value.state.uploading != nil {
  145. node.Value.state.uploading.isDeleted = true
  146. }
  147. }
  148. ch, err := createNewCacheFile(c, pathComps)
  149. if err != nil {
  150. logger.Warnf("create new cache file %v: %v", pathComps, err)
  151. return nil
  152. }
  153. ch.IncRef()
  154. c.activeCache.CreateWords(pathComps).Value = ch
  155. logger.Debugf("create new cache file %v", pathComps)
  156. return ch
  157. }
  158. // 尝试加载缓存文件,如果文件不存在,则使用obj的信息创建一个新缓存文件,而如果obj为nil,那么会返回nil。
  159. //
  160. // 记得使用Release减少引用计数
  161. func (c *Cache) LoadFile(pathComps []string, obj *clitypes.Object) *CacheFile {
  162. c.lock.Lock()
  163. defer c.lock.Unlock()
  164. node, ok := c.activeCache.WalkEnd(pathComps)
  165. if ok && node.Value != nil {
  166. node.Value.IncRef()
  167. return node.Value
  168. }
  169. ch, err := loadCacheFile(c, pathComps)
  170. if err == nil {
  171. ch.remoteObj = obj
  172. ch.IncRef()
  173. c.activeCache.CreateWords(pathComps).Value = ch
  174. logger.Debugf("load cache %v", pathComps)
  175. return ch
  176. }
  177. if !os.IsNotExist(err) {
  178. // TODO 日志记录
  179. logger.Warnf("load cache %v: %v", pathComps, err)
  180. return nil
  181. }
  182. if obj == nil {
  183. return nil
  184. }
  185. ch, err = newCacheFileFromObject(c, pathComps, obj)
  186. if err != nil {
  187. logger.Warnf("create cache %v from object: %v", pathComps, err)
  188. return nil
  189. }
  190. ch.IncRef()
  191. c.activeCache.CreateWords(pathComps).Value = ch
  192. logger.Debugf("create cache %v from object %v", pathComps, obj.ObjectID)
  193. return ch
  194. }
  195. // 创建一个缓存目录。如果目录已经存在,则会重置目录属性。如果加载过程中发生了错误,或者目标位置是一个文件,则会返回nil
  196. func (c *Cache) CreateDir(pathComps []string) *CacheDir {
  197. c.lock.Lock()
  198. defer c.lock.Unlock()
  199. ch, err := createNewCacheDir(c, pathComps)
  200. if err != nil {
  201. logger.Warnf("create cache dir: %v", err)
  202. return nil
  203. }
  204. return ch
  205. }
  206. type CreateDirOption struct {
  207. ModTime time.Time
  208. }
  209. // 加载指定缓存目录,如果目录不存在,则使用createOpt选项创建目录,而如果createOpt为nil,那么会返回nil。
  210. func (c *Cache) LoadDir(pathComps []string, createOpt *CreateDirOption) *CacheDir {
  211. c.lock.Lock()
  212. defer c.lock.Unlock()
  213. ch, err := loadCacheDir(c, pathComps)
  214. if err == nil {
  215. return ch
  216. }
  217. if !os.IsNotExist(err) {
  218. // TODO 日志记录
  219. return nil
  220. }
  221. if createOpt == nil {
  222. return nil
  223. }
  224. // 创建目录
  225. ch, err = makeCacheDirFromOption(c, pathComps, *createOpt)
  226. if err != nil {
  227. // TODO 日志记录
  228. return nil
  229. }
  230. return ch
  231. }
  232. // 加载指定路径下的所有缓存条目信息
  233. func (c *Cache) StatMany(pathComps []string) []CacheEntryInfo {
  234. c.lock.RLock()
  235. defer c.lock.RUnlock()
  236. var infos []CacheEntryInfo
  237. exists := make(map[string]bool)
  238. node, ok := c.activeCache.WalkEnd(pathComps)
  239. if ok {
  240. for name, child := range node.WordNexts {
  241. if child.Value != nil {
  242. infos = append(infos, child.Value.Info())
  243. exists[name] = true
  244. }
  245. }
  246. }
  247. osEns, err := os.ReadDir(c.GetCacheDataPath(pathComps...))
  248. if err != nil {
  249. return nil
  250. }
  251. for _, e := range osEns {
  252. if exists[e.Name()] {
  253. continue
  254. }
  255. info, err := e.Info()
  256. if err != nil {
  257. continue
  258. }
  259. if e.IsDir() {
  260. info, err := loadCacheDirInfo(c, append(lo2.ArrayClone(pathComps), e.Name()), info)
  261. if err != nil {
  262. continue
  263. }
  264. infos = append(infos, *info)
  265. } else {
  266. info, err := loadCacheFileInfo(c, append(lo2.ArrayClone(pathComps), e.Name()), info)
  267. if err != nil {
  268. continue
  269. }
  270. infos = append(infos, *info)
  271. }
  272. }
  273. return infos
  274. }
  275. // 删除指定路径的缓存文件或目录。删除目录时如果目录不为空,则会报错。
  276. func (c *Cache) Remove(pathComps []string) error {
  277. c.lock.Lock()
  278. defer c.lock.Unlock()
  279. node, ok := c.activeCache.WalkEnd(pathComps)
  280. if ok {
  281. if len(node.WordNexts) > 0 {
  282. return fuse.ErrNotEmpty
  283. }
  284. if node.Value != nil {
  285. node.Value.Delete()
  286. if node.Value.state.uploading != nil {
  287. node.Value.state.uploading.isDeleted = true
  288. }
  289. }
  290. node.RemoveSelf(true)
  291. logger.Debugf("active cache %v removed", pathComps)
  292. return nil
  293. }
  294. metaPath := c.GetCacheMetaPath(pathComps...)
  295. dataPath := c.GetCacheDataPath(pathComps...)
  296. os.Remove(metaPath)
  297. err := os.Remove(dataPath)
  298. if err == nil || os.IsNotExist(err) {
  299. logger.Debugf("local cache %v removed", pathComps)
  300. return nil
  301. }
  302. if errors.Is(err, syscall.ENOTEMPTY) {
  303. return fuse.ErrNotEmpty
  304. }
  305. return err
  306. }
  307. // 移动指定路径的缓存文件或目录到新的路径。如果目标路径已经存在,则会报错。
  308. //
  309. // 如果移动成功,则返回移动后的缓存文件或目录。如果文件或目录不存在,则返回nil。
  310. func (c *Cache) Move(pathComps []string, newPathComps []string) error {
  311. c.lock.Lock()
  312. defer c.lock.Unlock()
  313. _, ok := c.activeCache.WalkEnd(newPathComps)
  314. if ok {
  315. return fuse.ErrExists
  316. }
  317. newMetaPath := c.GetCacheMetaPath(newPathComps...)
  318. newDataPath := c.GetCacheDataPath(newPathComps...)
  319. _, err := os.Stat(newDataPath)
  320. if err == nil {
  321. return fuse.ErrExists
  322. }
  323. if !os.IsNotExist(err) {
  324. return err
  325. }
  326. oldMetaPath := c.GetCacheMetaPath(pathComps...)
  327. oldDataPath := c.GetCacheDataPath(pathComps...)
  328. // 确定源文件存在,再进行后面的操作
  329. _, err = os.Stat(oldDataPath)
  330. if err != nil {
  331. if os.IsNotExist(err) {
  332. return fuse.ErrNotExists
  333. }
  334. return err
  335. }
  336. // 创建父目录是为了解决被移动的文件不在本地的问题。
  337. // 但同时也导致了如果目的路径的父目录确实不存在,这里会意外的创建了这个目录
  338. newMetaDir := filepath.Dir(newMetaPath)
  339. err = os.MkdirAll(newMetaDir, 0755)
  340. if err != nil {
  341. return err
  342. }
  343. newDataDir := filepath.Dir(newDataPath)
  344. err = os.MkdirAll(newDataDir, 0755)
  345. if err != nil {
  346. return err
  347. }
  348. // 每个缓存文件持有meta文件和data文件的句柄,所以这里移动文件,不影响句柄的使用。
  349. // 只能忽略这里的错误
  350. os.Rename(oldMetaPath, newMetaPath)
  351. os.Rename(oldDataPath, newDataPath)
  352. // 更新缓存
  353. oldNode, ok := c.activeCache.WalkEnd(pathComps)
  354. if ok {
  355. newNode := c.activeCache.CreateWords(newPathComps)
  356. newNode.Value = oldNode.Value
  357. newNode.WordNexts = oldNode.WordNexts
  358. oldNode.RemoveSelf(false)
  359. if newNode.Value != nil {
  360. newNode.Value.Move(newPathComps)
  361. }
  362. newNode.Iterate(func(path []string, node *trie.Node[*CacheFile], isWordNode bool) trie.VisitCtrl {
  363. if node.Value != nil {
  364. node.Value.Move(lo2.AppendNew(newPathComps, path...))
  365. }
  366. return trie.VisitContinue
  367. })
  368. }
  369. logger.Debugf("cache moved: %v -> %v", pathComps, newPathComps)
  370. return nil
  371. }
  372. type syncPackage struct {
  373. bktName string
  374. pkgName string
  375. pkg clitypes.Package
  376. upObjs []*uploadingObject
  377. }
  378. type uploadingObject struct {
  379. pathComps []string
  380. cache *CacheFile
  381. reader *CacheFileHandle
  382. modTime time.Time
  383. metaRevision int
  384. isDeleted bool
  385. isSuccess bool
  386. }
  387. type packageFullName struct {
  388. bktName string
  389. pkgName string
  390. }
  391. func (c *Cache) scanningCache() {
  392. ticker := time.NewTicker(time.Second * 5)
  393. defer ticker.Stop()
  394. lastScanPath := []string{}
  395. for {
  396. select {
  397. case _, ok := <-c.cacheDone:
  398. if !ok {
  399. return
  400. }
  401. case <-ticker.C:
  402. }
  403. c.lock.Lock()
  404. uploadingPkgs := make(map[packageFullName]*syncPackage)
  405. visitCnt := 0
  406. visitBreak := false
  407. node, _ := c.activeCache.WalkEnd(lastScanPath)
  408. node.Iterate(func(path []string, node *trie.Node[*CacheFile], isWordNode bool) trie.VisitCtrl {
  409. ch := node.Value
  410. if ch == nil {
  411. return trie.VisitContinue
  412. }
  413. info := ch.Info()
  414. if info.RefCount > 0 {
  415. logger.Debugf("skip cache %v, refCount: %v", path, info.RefCount)
  416. return trie.VisitContinue
  417. }
  418. visitCnt++
  419. c.visitNode(path, node, ch, info, uploadingPkgs)
  420. // 每次最多遍历500个节点,防止占用锁太久
  421. if visitCnt > 500 {
  422. lastScanPath = lo2.ArrayClone(path)
  423. visitBreak = true
  424. return trie.VisitBreak
  425. }
  426. return trie.VisitContinue
  427. })
  428. if !visitBreak {
  429. lastScanPath = []string{}
  430. }
  431. c.lock.Unlock()
  432. if len(uploadingPkgs) > 0 {
  433. go c.doSync(lo.Values(uploadingPkgs))
  434. }
  435. }
  436. }
  437. func (c *Cache) visitNode(path []string, node *trie.Node[*CacheFile], ch *CacheFile, info CacheEntryInfo, uploadingPkgs map[packageFullName]*syncPackage) {
  438. shouldUpload := true
  439. // 不存放在Package里的文件,不需要上传
  440. if len(ch.pathComps) <= 2 {
  441. shouldUpload = false
  442. }
  443. // 1. 本地缓存被修改了,如果一段时间内没有被使用,则进行上传
  444. if shouldUpload && (info.DataRevision > 0 || info.MetaRevision > 0) {
  445. if time.Since(info.FreeTime) < c.cfg.UploadPendingTime {
  446. return
  447. }
  448. if ch.state.uploading != nil {
  449. return
  450. }
  451. fullName := packageFullName{ch.pathComps[0], ch.pathComps[1]}
  452. pkg, ok := uploadingPkgs[fullName]
  453. if !ok {
  454. pkg = &syncPackage{
  455. bktName: ch.pathComps[0],
  456. pkgName: ch.pathComps[1],
  457. }
  458. uploadingPkgs[fullName] = pkg
  459. }
  460. obj := &uploadingObject{
  461. pathComps: lo2.ArrayClone(ch.pathComps),
  462. cache: ch,
  463. }
  464. pkg.upObjs = append(pkg.upObjs, obj)
  465. ch.state.uploading = obj
  466. if info.DataRevision > 0 {
  467. obj.reader = ch.OpenReadWhenScanning()
  468. }
  469. if info.MetaRevision > 0 {
  470. obj.modTime = info.ModTime
  471. obj.metaRevision = info.MetaRevision
  472. }
  473. return
  474. }
  475. // 2. 本地缓存没有被修改,如果一段时间内没有被使用,则进行卸载
  476. if info.Level > LevelReadOnly {
  477. if time.Since(info.FreeTime) > c.cfg.CacheActiveTime {
  478. ch.LevelDown(LevelReadOnly)
  479. }
  480. return
  481. }
  482. // 3. 卸载后的缓存,如果一段时间内没有被使用,则进行删除。
  483. if info.Level <= LevelReadOnly {
  484. // 需要同时满足距上次使用时间和距上次卸载时间超过配置的时间,才可以删除
  485. if time.Since(info.FreeTime) > c.cfg.CacheExpireTime && time.Since(info.ChangeLevelTime) > c.cfg.CacheExpireTime {
  486. // 如果文件已经同步到远端,则可以直接删除本地缓存
  487. if info.MetaRevision == 0 && info.DataRevision == 0 {
  488. ch.Delete()
  489. }
  490. node.RemoveSelf(true)
  491. }
  492. return
  493. }
  494. }
  495. func (c *Cache) scanningData() {
  496. ticker := time.NewTicker(c.cfg.ScanDataDirInterval)
  497. defer ticker.Stop()
  498. var walkTrace []*os.File
  499. var walkTraceComps []string
  500. for {
  501. select {
  502. case <-ticker.C:
  503. case <-c.cacheDone:
  504. return
  505. }
  506. logger.Infof("begin scanning data dir")
  507. if len(walkTrace) == 0 {
  508. dir, err := os.Open(c.cfg.DataDir)
  509. if err != nil {
  510. logger.Warnf("open data dir: %v", err)
  511. continue
  512. }
  513. walkTrace = []*os.File{dir}
  514. walkTraceComps = []string{c.cfg.MetaDir}
  515. }
  516. const maxVisitCnt = 5000
  517. const maxUntrackedFiles = 500
  518. var untrackedFiles [][]string
  519. visitCnt := 0
  520. // 一次最多遍历5000个文件(包括路径上的文件夹),一次最多添加500个未跟踪文件
  521. for len(walkTrace) > 0 && visitCnt < maxVisitCnt && len(untrackedFiles) < maxUntrackedFiles {
  522. lastNode := walkTrace[len(walkTrace)-1]
  523. visitCnt++
  524. e, err := lastNode.Readdir(1)
  525. if err == io.EOF {
  526. lastNode.Close()
  527. walkTrace = walkTrace[:len(walkTrace)-1]
  528. walkTraceComps = walkTraceComps[:len(walkTraceComps)-1]
  529. continue
  530. }
  531. if err != nil {
  532. logger.Warnf("read dir %v: %v", lastNode.Name(), err)
  533. lastNode.Close()
  534. walkTrace = walkTrace[:len(walkTrace)-1]
  535. walkTraceComps = walkTraceComps[:len(walkTraceComps)-1]
  536. continue
  537. }
  538. if e[0].IsDir() {
  539. child, err := os.Open(filepath.Join(lastNode.Name(), e[0].Name()))
  540. if err != nil {
  541. logger.Warnf("open dir %v: %v", e[0].Name(), err)
  542. continue
  543. }
  544. walkTrace = append(walkTrace, child)
  545. walkTraceComps = append(walkTraceComps, e[0].Name())
  546. continue
  547. }
  548. // 对于不在Package层级的文件,不跟踪
  549. if len(walkTrace) <= 2 {
  550. continue
  551. }
  552. walkTraceComps = append(walkTraceComps, e[0].Name())
  553. fileMetaPath := filepath.Join(walkTraceComps...)
  554. _, err = os.Stat(fileMetaPath)
  555. if err == nil || !os.IsNotExist(err) {
  556. walkTraceComps = walkTraceComps[:len(walkTraceComps)-1]
  557. continue
  558. }
  559. untrackedFiles = append(untrackedFiles, lo2.ArrayClone(walkTraceComps[1:]))
  560. walkTraceComps = walkTraceComps[:len(walkTraceComps)-1]
  561. }
  562. if len(untrackedFiles) > 0 {
  563. for _, comps := range untrackedFiles {
  564. ch := c.LoadFile(comps, nil)
  565. if ch != nil {
  566. ch.Release()
  567. }
  568. }
  569. }
  570. logger.Infof("%v file visited, %v untracked files found", visitCnt, len(untrackedFiles))
  571. }
  572. }
  573. func (c *Cache) doSync(pkgs []*syncPackage) {
  574. var uploadPkgs []*syncPackage
  575. var updateOnlyPkgs []*syncPackage
  576. for _, p := range pkgs {
  577. var updateOnly *syncPackage
  578. var upload *syncPackage
  579. for _, o := range p.upObjs {
  580. if o.reader != nil {
  581. if upload == nil {
  582. upload = &syncPackage{
  583. bktName: p.bktName,
  584. pkgName: p.pkgName,
  585. }
  586. }
  587. upload.upObjs = append(upload.upObjs, o)
  588. } else {
  589. if updateOnly == nil {
  590. updateOnly = &syncPackage{
  591. bktName: p.bktName,
  592. pkgName: p.pkgName,
  593. }
  594. }
  595. updateOnly.upObjs = append(updateOnly.upObjs, o)
  596. }
  597. }
  598. if upload != nil {
  599. uploadPkgs = append(uploadPkgs, upload)
  600. }
  601. if updateOnly != nil {
  602. updateOnlyPkgs = append(updateOnlyPkgs, updateOnly)
  603. }
  604. }
  605. // 先上传文件,再更新文件元数据。上传文件时会创建Package,这样后续更新元数据时就能查到Package。
  606. if len(uploadPkgs) > 0 {
  607. c.doUploading(uploadPkgs)
  608. }
  609. if len(updateOnlyPkgs) > 0 {
  610. c.doUpdatingOnly(updateOnlyPkgs)
  611. }
  612. }
  613. func (c *Cache) doUpdatingOnly(pkgs []*syncPackage) {
  614. /// 1. 只是更新元数据,那么就只尝试查询Package
  615. var sucPkgs []*syncPackage
  616. var failedPkgs []*syncPackage
  617. for _, pkg := range pkgs {
  618. p, err := c.db.Package().GetByFullName(c.db.DefCtx(), pkg.bktName, pkg.pkgName)
  619. if err != nil {
  620. logger.Warnf("get package %v/%v: %v", pkg.bktName, pkg.pkgName, err)
  621. failedPkgs = append(failedPkgs, pkg)
  622. continue
  623. }
  624. pkg.pkg = p
  625. sucPkgs = append(sucPkgs, pkg)
  626. }
  627. /// 2. 对于创建失败的Package, 在锁的保护下取消上传状态
  628. c.lock.Lock()
  629. for _, pkg := range failedPkgs {
  630. for _, obj := range pkg.upObjs {
  631. obj.cache.state.uploading = nil
  632. }
  633. }
  634. c.lock.Unlock()
  635. /// 3. 开始更新每个Package
  636. for _, p := range sucPkgs {
  637. pathes := make([]string, 0, len(p.upObjs))
  638. modTimes := make([]time.Time, 0, len(p.upObjs))
  639. for _, obj := range p.upObjs {
  640. pathes = append(pathes, clitypes.JoinObjectPath(obj.pathComps[2:]...))
  641. modTimes = append(modTimes, obj.modTime)
  642. }
  643. err := c.db.Object().BatchUpdateUpdateTimeByPath(c.db.DefCtx(), p.pkg.PackageID, pathes, modTimes)
  644. if err != nil {
  645. logger.Warnf("batch update package %v/%v: %v", p.bktName, p.pkgName, err)
  646. c.lock.Lock()
  647. for _, obj := range p.upObjs {
  648. obj.cache.state.uploading = nil
  649. }
  650. c.lock.Unlock()
  651. continue
  652. }
  653. logger.Infof("update %v object in package %v/%v", len(p.upObjs), p.bktName, p.pkgName)
  654. // 登记上传结果
  655. c.lock.Lock()
  656. for _, obj := range p.upObjs {
  657. obj.cache.state.uploading = nil
  658. obj.cache.RevisionUploaded(0, obj.metaRevision)
  659. }
  660. c.lock.Unlock()
  661. }
  662. }
  663. func (c *Cache) doUploading(pkgs []*syncPackage) {
  664. /// 1. 先尝试创建Package
  665. var sucPkgs []*syncPackage
  666. var failedPkgs []*syncPackage
  667. for _, pkg := range pkgs {
  668. p, err := db.DoTx21(c.db, c.db.Package().TryCreateAll, pkg.bktName, pkg.pkgName)
  669. if err != nil {
  670. logger.Warnf("try create package %v/%v: %v", pkg.bktName, pkg.pkgName, err)
  671. failedPkgs = append(failedPkgs, pkg)
  672. continue
  673. }
  674. pkg.pkg = p
  675. sucPkgs = append(sucPkgs, pkg)
  676. }
  677. /// 2. 对于创建失败的Package,直接关闭文件,不进行上传
  678. // 在锁的保护下取消上传状态
  679. c.lock.Lock()
  680. for _, pkg := range failedPkgs {
  681. for _, obj := range pkg.upObjs {
  682. obj.cache.state.uploading = nil
  683. }
  684. }
  685. c.lock.Unlock()
  686. for _, pkg := range failedPkgs {
  687. for _, obj := range pkg.upObjs {
  688. obj.reader.Close()
  689. }
  690. }
  691. /// 3. 开始上传每个Package
  692. for _, p := range sucPkgs {
  693. upder, err := c.uploader.BeginUpdate(p.pkg.PackageID, 0, nil, nil)
  694. if err != nil {
  695. logger.Warnf("begin upload package %v/%v: %v", p.bktName, p.pkgName, err)
  696. // 取消上传状态
  697. c.lock.Lock()
  698. for _, obj := range p.upObjs {
  699. obj.cache.state.uploading = nil
  700. }
  701. c.lock.Unlock()
  702. for _, obj := range p.upObjs {
  703. obj.reader.Close()
  704. }
  705. continue
  706. }
  707. upSuc := 0
  708. upSucAmt := int64(0)
  709. upFailed := 0
  710. upStartTime := time.Now()
  711. logger.Infof("begin uploading %v objects to package %v/%v", len(p.upObjs), p.bktName, p.pkgName)
  712. for _, o := range p.upObjs {
  713. rd := cacheFileReader{
  714. rw: o.reader,
  715. }
  716. counter := io2.Counter(&rd)
  717. err = upder.Upload(clitypes.JoinObjectPath(o.pathComps[2:]...), counter, uploader.UploadOption{
  718. CreateTime: o.modTime,
  719. })
  720. if err != nil {
  721. logger.Warnf("upload object %v: %v", o.pathComps, err)
  722. upFailed++
  723. continue
  724. }
  725. o.isSuccess = true
  726. upSuc++
  727. upSucAmt += counter.Count()
  728. }
  729. // 在锁保护下登记上传结果
  730. c.lock.Lock()
  731. upCancel := 0
  732. upRename := 0
  733. // 检查是否有文件在上传期间发生了变化
  734. var sucObjs []*uploadingObject
  735. for _, o := range p.upObjs {
  736. o.cache.state.uploading = nil
  737. if !o.isSuccess {
  738. continue
  739. }
  740. oldPath := clitypes.JoinObjectPath(o.pathComps[2:]...)
  741. newPath := clitypes.JoinObjectPath(o.cache.pathComps[2:]...)
  742. if o.isDeleted {
  743. upder.CancelObject(oldPath)
  744. upCancel++
  745. continue
  746. }
  747. // 如果对象移动到了另一个Package,那么也要取消上传
  748. if !lo2.ArrayEquals(o.pathComps[:2], o.cache.pathComps[:2]) {
  749. upder.CancelObject(oldPath)
  750. upCancel++
  751. continue
  752. }
  753. // 只有仍在同Package内移动的对象才能直接重命名
  754. if newPath != oldPath {
  755. upder.RenameObject(oldPath, newPath)
  756. upRename++
  757. }
  758. sucObjs = append(sucObjs, o)
  759. }
  760. _, err = upder.Commit()
  761. if err != nil {
  762. logger.Warnf("commit update package %v/%v: %v", p.bktName, p.pkgName, err)
  763. } else {
  764. for _, obj := range sucObjs {
  765. obj.cache.RevisionUploaded(obj.reader.revision, obj.metaRevision)
  766. }
  767. upTime := time.Since(upStartTime)
  768. logger.Infof("upload package %v/%v in %v, upload: %v, size: %v, speed: %v/s, cancel: %v, rename: %v",
  769. p.bktName, p.pkgName, upTime, upSuc, upSucAmt, bytesize.New(float64(upSucAmt)/upTime.Seconds()), upCancel, upRename)
  770. }
  771. c.lock.Unlock()
  772. // 关闭文件会影响refCount,所以无论是上传失败还是上传成功,都会在等待一段时间后才进行下一阶段的操作
  773. for _, obj := range p.upObjs {
  774. obj.reader.Close()
  775. }
  776. }
  777. }
  778. type cacheFileReader struct {
  779. rw *CacheFileHandle
  780. pos int64
  781. }
  782. func (r *cacheFileReader) Read(p []byte) (int, error) {
  783. n, err := r.rw.ReadAt(p, r.pos)
  784. r.pos += int64(n)
  785. if err != nil {
  786. return n, err
  787. }
  788. if n != len(p) {
  789. return n, io.EOF
  790. }
  791. return n, nil
  792. }

本项目旨在将云际存储公共基础设施化,使个人及企业可低门槛使用高效的云际存储服务(安装开箱即用云际存储客户端即可,无需关注其他组件的部署),同时支持用户灵活便捷定制云际存储的功能细节。