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.7 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  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(Shard{})
  86. if err != nil {
  87. return nil, fmt.Errorf("migrate Shard: %w", err)
  88. }
  89. err = db.AutoMigrate(UserRef{})
  90. if err != nil {
  91. return nil, fmt.Errorf("migrate UserRef: %w", err)
  92. }
  93. ss.Start(p.stgEventChan)
  94. loaded = &LoadedStore{
  95. ShardStore: ss,
  96. Config: resp.PubShards,
  97. PasswordHash: pwdHash,
  98. ClientFileHashDB: db,
  99. }
  100. p.stores[pubStoreID] = loaded
  101. log.Infof("%v loaded", loaded.Config.String())
  102. } else {
  103. // 如果已经被加载,那么就要验证一下密码是否正确
  104. if bcrypt.CompareHashAndPassword(loaded.PasswordHash, []byte(password)) != nil {
  105. return nil, fmt.Errorf("wrong password")
  106. }
  107. }
  108. return loaded, nil
  109. }
  110. func (p *Pool) Start() {
  111. go func() {
  112. log := logger.WithField("Mod", "PubShards")
  113. ticker := time.NewTicker(time.Minute)
  114. defer ticker.Stop()
  115. gced := make(map[jcstypes.PubShardsID]bool)
  116. loop:
  117. for {
  118. select {
  119. case <-ticker.C:
  120. case <-p.done:
  121. break loop
  122. }
  123. // 凌晨5点开始GC
  124. if time.Now().Hour() != 5 {
  125. gced = make(map[jcstypes.PubShardsID]bool)
  126. continue
  127. }
  128. p.lock.Lock()
  129. for pubStoreID, loaded := range p.stores {
  130. if gced[pubStoreID] {
  131. continue
  132. }
  133. allHashes, err := loaded.GetAllHashes()
  134. if err != nil {
  135. log.Warnf("get all hashes of %v: %v", loaded.Config.String(), err)
  136. continue
  137. }
  138. err = loaded.ShardStore.GC(allHashes)
  139. if err != nil {
  140. log.Warnf("gc %v: %v", loaded.Config.String(), err)
  141. continue
  142. }
  143. gced[pubStoreID] = true
  144. log.Infof("%v gc done", loaded.Config.String())
  145. }
  146. p.lock.Unlock()
  147. }
  148. p.lock.Lock()
  149. for _, loaded := range p.stores {
  150. loaded.ShardStore.Stop()
  151. d, err := loaded.ClientFileHashDB.DB()
  152. if err != nil {
  153. log.Warnf("get sql db of %v: %v", loaded.Config.String(), err)
  154. continue
  155. }
  156. d.Close()
  157. }
  158. p.stores = make(map[jcstypes.PubShardsID]*LoadedStore)
  159. p.lock.Unlock()
  160. }()
  161. }
  162. func (p *Pool) Stop() {
  163. select {
  164. case p.done <- true:
  165. default:
  166. }
  167. }

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