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.

package.go 8.4 kB

2 years ago
2 years ago
2 years ago
2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. package mq
  2. import (
  3. "database/sql"
  4. "fmt"
  5. "sort"
  6. "github.com/jmoiron/sqlx"
  7. "gitlink.org.cn/cloudream/common/consts/errorcode"
  8. "gitlink.org.cn/cloudream/common/pkgs/logger"
  9. "gitlink.org.cn/cloudream/common/pkgs/mq"
  10. cdssdk "gitlink.org.cn/cloudream/common/sdks/storage"
  11. coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator"
  12. )
  13. func (svc *Service) GetPackage(msg *coormq.GetPackage) (*coormq.GetPackageResp, *mq.CodeMessage) {
  14. pkg, err := svc.db.Package().GetByID(svc.db.SQLCtx(), msg.PackageID)
  15. if err != nil {
  16. logger.WithField("PackageID", msg.PackageID).
  17. Warnf("get package: %s", err.Error())
  18. return nil, mq.Failed(errorcode.OperationFailed, "get package failed")
  19. }
  20. return mq.ReplyOK(coormq.NewGetPackageResp(pkg))
  21. }
  22. func (svc *Service) GetPackageByName(msg *coormq.GetPackageByName) (*coormq.GetPackageByNameResp, *mq.CodeMessage) {
  23. pkg, err := svc.db.Package().GetUserPackageByName(svc.db.SQLCtx(), msg.UserID, msg.BucketName, msg.PackageName)
  24. if err != nil {
  25. logger.WithField("UserID", msg.UserID).
  26. WithField("BucketName", msg.BucketName).
  27. WithField("PackageName", msg.PackageName).
  28. Warnf("get package by name: %s", err.Error())
  29. if err == sql.ErrNoRows {
  30. return nil, mq.Failed(errorcode.DataNotFound, "package not found")
  31. }
  32. return nil, mq.Failed(errorcode.OperationFailed, "get package by name failed")
  33. }
  34. return mq.ReplyOK(coormq.NewGetPackageByNameResp(pkg))
  35. }
  36. func (svc *Service) CreatePackage(msg *coormq.CreatePackage) (*coormq.CreatePackageResp, *mq.CodeMessage) {
  37. var pkg cdssdk.Package
  38. err := svc.db.DoTx(sql.LevelSerializable, func(tx *sqlx.Tx) error {
  39. var err error
  40. isAvai, _ := svc.db.Bucket().IsAvailable(tx, msg.BucketID, msg.UserID)
  41. if !isAvai {
  42. return fmt.Errorf("bucket is not avaiable to the user")
  43. }
  44. pkgID, err := svc.db.Package().Create(tx, msg.BucketID, msg.Name)
  45. if err != nil {
  46. return fmt.Errorf("creating package: %w", err)
  47. }
  48. pkg, err = svc.db.Package().GetByID(tx, pkgID)
  49. if err != nil {
  50. return fmt.Errorf("getting package by id: %w", err)
  51. }
  52. return nil
  53. })
  54. if err != nil {
  55. logger.WithField("BucketID", msg.BucketID).
  56. WithField("Name", msg.Name).
  57. Warn(err.Error())
  58. return nil, mq.Failed(errorcode.OperationFailed, "creating package failed")
  59. }
  60. return mq.ReplyOK(coormq.NewCreatePackageResp(pkg))
  61. }
  62. func (svc *Service) UpdatePackage(msg *coormq.UpdatePackage) (*coormq.UpdatePackageResp, *mq.CodeMessage) {
  63. var added []cdssdk.Object
  64. err := svc.db.DoTx(sql.LevelSerializable, func(tx *sqlx.Tx) error {
  65. _, err := svc.db.Package().GetByID(tx, msg.PackageID)
  66. if err != nil {
  67. return fmt.Errorf("getting package by id: %w", err)
  68. }
  69. // 先执行删除操作
  70. if len(msg.Deletes) > 0 {
  71. if err := svc.db.Object().BatchDelete(tx, msg.Deletes); err != nil {
  72. return fmt.Errorf("deleting objects: %w", err)
  73. }
  74. }
  75. // 再执行添加操作
  76. if len(msg.Adds) > 0 {
  77. ad, err := svc.db.Object().BatchAdd(tx, msg.PackageID, msg.Adds)
  78. if err != nil {
  79. return fmt.Errorf("adding objects: %w", err)
  80. }
  81. added = ad
  82. }
  83. return nil
  84. })
  85. if err != nil {
  86. logger.WithField("PackageID", msg.PackageID).Warn(err.Error())
  87. return nil, mq.Failed(errorcode.OperationFailed, "update package failed")
  88. }
  89. return mq.ReplyOK(coormq.NewUpdatePackageResp(added))
  90. }
  91. func (svc *Service) DeletePackage(msg *coormq.DeletePackage) (*coormq.DeletePackageResp, *mq.CodeMessage) {
  92. err := svc.db.DoTx(sql.LevelSerializable, func(tx *sqlx.Tx) error {
  93. isAvai, _ := svc.db.Package().IsAvailable(tx, msg.UserID, msg.PackageID)
  94. if !isAvai {
  95. return fmt.Errorf("package is not available to the user")
  96. }
  97. err := svc.db.Package().SoftDelete(tx, msg.PackageID)
  98. if err != nil {
  99. return fmt.Errorf("soft delete package: %w", err)
  100. }
  101. err = svc.db.Package().DeleteUnused(tx, msg.PackageID)
  102. if err != nil {
  103. logger.WithField("UserID", msg.UserID).
  104. WithField("PackageID", msg.PackageID).
  105. Warnf("deleting unused package: %w", err.Error())
  106. }
  107. err = svc.db.PackageAccessStat().DeleteByPackageID(tx, msg.PackageID)
  108. if err != nil {
  109. logger.WithField("UserID", msg.UserID).
  110. WithField("PackageID", msg.PackageID).
  111. Warnf("deleting package access stat: %w", err.Error())
  112. }
  113. return nil
  114. })
  115. if err != nil {
  116. logger.WithField("UserID", msg.UserID).
  117. WithField("PackageID", msg.PackageID).
  118. Warnf(err.Error())
  119. return nil, mq.Failed(errorcode.OperationFailed, "delete package failed")
  120. }
  121. return mq.ReplyOK(coormq.NewDeletePackageResp())
  122. }
  123. func (svc *Service) GetPackageCachedNodes(msg *coormq.GetPackageCachedNodes) (*coormq.GetPackageCachedNodesResp, *mq.CodeMessage) {
  124. isAva, err := svc.db.Package().IsAvailable(svc.db.SQLCtx(), msg.UserID, msg.PackageID)
  125. if err != nil {
  126. logger.WithField("UserID", msg.UserID).
  127. WithField("PackageID", msg.PackageID).
  128. Warnf("check package available failed, err: %s", err.Error())
  129. return nil, mq.Failed(errorcode.OperationFailed, "check package available failed")
  130. }
  131. if !isAva {
  132. logger.WithField("UserID", msg.UserID).
  133. WithField("PackageID", msg.PackageID).
  134. Warnf("package is not available to the user")
  135. return nil, mq.Failed(errorcode.OperationFailed, "package is not available to the user")
  136. }
  137. // 这个函数只是统计哪些节点缓存了Package中的数据,不需要多么精确,所以可以不用事务
  138. objDetails, err := svc.db.Object().GetPackageObjectDetails(svc.db.SQLCtx(), msg.PackageID)
  139. if err != nil {
  140. logger.WithField("PackageID", msg.PackageID).
  141. Warnf("get package block details: %s", err.Error())
  142. return nil, mq.Failed(errorcode.OperationFailed, "get package block details failed")
  143. }
  144. var packageSize int64
  145. nodeInfoMap := make(map[cdssdk.NodeID]*cdssdk.NodePackageCachingInfo)
  146. for _, obj := range objDetails {
  147. // 只要存了文件的一个块,就认为此节点存了整个文件
  148. for _, block := range obj.Blocks {
  149. info, ok := nodeInfoMap[block.NodeID]
  150. if !ok {
  151. info = &cdssdk.NodePackageCachingInfo{
  152. NodeID: block.NodeID,
  153. }
  154. nodeInfoMap[block.NodeID] = info
  155. }
  156. info.FileSize += obj.Object.Size
  157. info.ObjectCount++
  158. }
  159. }
  160. var nodeInfos []cdssdk.NodePackageCachingInfo
  161. for _, nodeInfo := range nodeInfoMap {
  162. nodeInfos = append(nodeInfos, *nodeInfo)
  163. }
  164. sort.Slice(nodeInfos, func(i, j int) bool {
  165. return nodeInfos[i].NodeID < nodeInfos[j].NodeID
  166. })
  167. return mq.ReplyOK(coormq.NewGetPackageCachedNodesResp(nodeInfos, packageSize))
  168. }
  169. func (svc *Service) GetPackageLoadedNodes(msg *coormq.GetPackageLoadedNodes) (*coormq.GetPackageLoadedNodesResp, *mq.CodeMessage) {
  170. storages, err := svc.db.StoragePackage().FindPackageStorages(svc.db.SQLCtx(), msg.PackageID)
  171. if err != nil {
  172. logger.WithField("PackageID", msg.PackageID).
  173. Warnf("get storages by packageID failed, err: %s", err.Error())
  174. return nil, mq.Failed(errorcode.OperationFailed, "get storages by packageID failed")
  175. }
  176. uniqueNodeIDs := make(map[cdssdk.NodeID]bool)
  177. var nodeIDs []cdssdk.NodeID
  178. for _, stg := range storages {
  179. if !uniqueNodeIDs[stg.NodeID] {
  180. uniqueNodeIDs[stg.NodeID] = true
  181. nodeIDs = append(nodeIDs, stg.NodeID)
  182. }
  183. }
  184. return mq.ReplyOK(coormq.NewGetPackageLoadedNodesResp(nodeIDs))
  185. }
  186. func (svc *Service) AddAccessStat(msg *coormq.AddAccessStat) {
  187. pkgIDs := make([]cdssdk.PackageID, len(msg.Entries))
  188. objIDs := make([]cdssdk.ObjectID, len(msg.Entries))
  189. for i, e := range msg.Entries {
  190. pkgIDs[i] = e.PackageID
  191. objIDs[i] = e.ObjectID
  192. }
  193. err := svc.db.DoTx(sql.LevelSerializable, func(tx *sqlx.Tx) error {
  194. avaiPkgIDs, err := svc.db.Package().BatchTestPackageID(tx, pkgIDs)
  195. if err != nil {
  196. return fmt.Errorf("batch test package id: %w", err)
  197. }
  198. avaiObjIDs, err := svc.db.Object().BatchTestObjectID(tx, objIDs)
  199. if err != nil {
  200. return fmt.Errorf("batch test object id: %w", err)
  201. }
  202. var willAdds []coormq.AddAccessStatEntry
  203. for _, e := range msg.Entries {
  204. if avaiPkgIDs[e.PackageID] && avaiObjIDs[e.ObjectID] {
  205. willAdds = append(willAdds, e)
  206. }
  207. }
  208. if len(willAdds) > 0 {
  209. err := svc.db.PackageAccessStat().BatchAddCounter(tx, willAdds)
  210. if err != nil {
  211. return fmt.Errorf("batch add package access stat counter: %w", err)
  212. }
  213. err = svc.db.ObjectAccessStat().BatchAddCounter(tx, willAdds)
  214. if err != nil {
  215. return fmt.Errorf("batch add object access stat counter: %w", err)
  216. }
  217. }
  218. return nil
  219. })
  220. if err != nil {
  221. logger.Warn(err.Error())
  222. }
  223. }

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