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.

pool.go 4.5 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. package pubshards
  2. import (
  3. "context"
  4. "encoding/hex"
  5. "fmt"
  6. "os"
  7. "path/filepath"
  8. "sync"
  9. "time"
  10. "github.com/glebarez/sqlite"
  11. "gitlink.org.cn/cloudream/common/pkgs/async"
  12. "gitlink.org.cn/cloudream/common/pkgs/logger"
  13. stgglb "gitlink.org.cn/cloudream/jcs-pub/common/globals"
  14. corrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/coordinator"
  15. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/factory"
  16. stgtypes "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/types"
  17. jcstypes "gitlink.org.cn/cloudream/jcs-pub/common/types"
  18. "golang.org/x/crypto/bcrypt"
  19. "gorm.io/gorm"
  20. )
  21. type Pool struct {
  22. cfg Config
  23. localHubID jcstypes.HubID
  24. stores map[jcstypes.PubShardsID]*LoadedStore
  25. stgEventChan *stgtypes.StorageEventChan
  26. done chan any
  27. lock sync.Mutex
  28. }
  29. func New(cfg Config, localHubID jcstypes.HubID) *Pool {
  30. return &Pool{
  31. cfg: cfg,
  32. localHubID: localHubID,
  33. stores: make(map[jcstypes.PubShardsID]*LoadedStore),
  34. stgEventChan: async.NewUnboundChannel[stgtypes.StorageEvent](),
  35. done: make(chan any, 1),
  36. }
  37. }
  38. func (p *Pool) GetOrLoad(pubStoreID jcstypes.PubShardsID, password string) (*LoadedStore, error) {
  39. log := logger.WithField("Mod", "PubShards")
  40. p.lock.Lock()
  41. defer p.lock.Unlock()
  42. loaded := p.stores[pubStoreID]
  43. if loaded == nil {
  44. corCli := stgglb.CoordinatorRPCPool.Get()
  45. defer corCli.Release()
  46. resp, cerr := corCli.UserGetPubShards(context.Background(), &corrpc.UserGetPubShards{
  47. PubShardsID: pubStoreID,
  48. Password: password,
  49. })
  50. if cerr != nil {
  51. return nil, cerr.ToError()
  52. }
  53. if resp.PubShards.MasterHub != p.localHubID {
  54. return nil, fmt.Errorf("this hub is not the master hub of the public shard store")
  55. }
  56. pwdHash, err := hex.DecodeString(resp.PubShards.Password)
  57. if err != nil {
  58. return nil, fmt.Errorf("decode password: %w", err)
  59. }
  60. detail := jcstypes.UserSpaceDetail{
  61. UserSpace: jcstypes.UserSpace{
  62. Name: resp.PubShards.Name,
  63. Storage: resp.PubShards.Storage,
  64. Credential: resp.PubShards.Credential,
  65. ShardStore: &resp.PubShards.ShardStore,
  66. Features: resp.PubShards.Features,
  67. WorkingDir: resp.PubShards.WorkingDir,
  68. },
  69. RecommendHub: &resp.MasterHub,
  70. }
  71. blder := factory.GetBuilder(&detail)
  72. ss, err := blder.CreateShardStore(false)
  73. if err != nil {
  74. return nil, err
  75. }
  76. err = os.MkdirAll(p.cfg.DBDir, 0755)
  77. if err != nil {
  78. return nil, err
  79. }
  80. dbFilePath := filepath.Join(p.cfg.DBDir, fmt.Sprintf("%s.db", pubStoreID))
  81. db, err := gorm.Open(sqlite.Open(dbFilePath), &gorm.Config{})
  82. if err != nil {
  83. return nil, err
  84. }
  85. err = db.AutoMigrate(FileEntry{})
  86. if err != nil {
  87. return nil, err
  88. }
  89. ss.Start(p.stgEventChan)
  90. loaded = &LoadedStore{
  91. ShardStore: ss,
  92. Config: resp.PubShards,
  93. PasswordHash: pwdHash,
  94. ClientFileHashDB: db,
  95. }
  96. p.stores[pubStoreID] = loaded
  97. log.Infof("%v loaded", loaded.Config.String())
  98. } else {
  99. // 如果已经被加载,那么就要验证一下密码是否正确
  100. if bcrypt.CompareHashAndPassword(loaded.PasswordHash, []byte(password)) != nil {
  101. return nil, fmt.Errorf("wrong password")
  102. }
  103. }
  104. return loaded, nil
  105. }
  106. func (p *Pool) Start() {
  107. go func() {
  108. log := logger.WithField("Mod", "PubShards")
  109. ticker := time.NewTicker(time.Minute)
  110. defer ticker.Stop()
  111. gced := make(map[jcstypes.PubShardsID]bool)
  112. loop:
  113. for {
  114. select {
  115. case <-ticker.C:
  116. case <-p.done:
  117. break loop
  118. }
  119. // 凌晨5点开始GC
  120. if time.Now().Hour() != 5 {
  121. gced = make(map[jcstypes.PubShardsID]bool)
  122. continue
  123. }
  124. p.lock.Lock()
  125. for pubStoreID, loaded := range p.stores {
  126. if gced[pubStoreID] {
  127. continue
  128. }
  129. allHashes, err := loaded.GetAllHashes()
  130. if err != nil {
  131. log.Warnf("get all hashes of %v: %v", loaded.Config.String(), err)
  132. continue
  133. }
  134. err = loaded.ShardStore.GC(allHashes)
  135. if err != nil {
  136. log.Warnf("gc %v: %v", loaded.Config.String(), err)
  137. continue
  138. }
  139. gced[pubStoreID] = true
  140. log.Infof("%v gc done", loaded.Config.String())
  141. }
  142. p.lock.Unlock()
  143. }
  144. p.lock.Lock()
  145. for _, loaded := range p.stores {
  146. loaded.ShardStore.Stop()
  147. d, err := loaded.ClientFileHashDB.DB()
  148. if err != nil {
  149. log.Warnf("get sql db of %v: %v", loaded.Config.String(), err)
  150. continue
  151. }
  152. d.Close()
  153. }
  154. p.stores = make(map[jcstypes.PubShardsID]*LoadedStore)
  155. p.lock.Unlock()
  156. }()
  157. }
  158. func (p *Pool) Stop() {
  159. select {
  160. case p.done <- true:
  161. default:
  162. }
  163. }

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