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

11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. package mq
  2. import (
  3. "errors"
  4. "fmt"
  5. "sort"
  6. stgmod "gitlink.org.cn/cloudream/storage2/common/models"
  7. "gitlink.org.cn/cloudream/storage2/common/pkgs/db2"
  8. "gorm.io/gorm"
  9. "gitlink.org.cn/cloudream/common/consts/errorcode"
  10. "gitlink.org.cn/cloudream/common/pkgs/logger"
  11. "gitlink.org.cn/cloudream/common/pkgs/mq"
  12. cdssdk "gitlink.org.cn/cloudream/common/sdks/storage"
  13. coormq "gitlink.org.cn/cloudream/storage2/common/pkgs/mq/coordinator"
  14. )
  15. func (svc *Service) GetPackage(msg *coormq.GetPackage) (*coormq.GetPackageResp, *mq.CodeMessage) {
  16. pkg, err := svc.db2.Package().GetByID(svc.db2.DefCtx(), msg.PackageID)
  17. if err != nil {
  18. logger.WithField("PackageID", msg.PackageID).
  19. Warnf("get package: %s", err.Error())
  20. if errors.Is(err, gorm.ErrRecordNotFound) {
  21. return nil, mq.Failed(errorcode.DataNotFound, "package not found")
  22. }
  23. return nil, mq.Failed(errorcode.OperationFailed, "get package failed")
  24. }
  25. return mq.ReplyOK(coormq.NewGetPackageResp(pkg))
  26. }
  27. func (svc *Service) GetPackageByName(msg *coormq.GetPackageByName) (*coormq.GetPackageByNameResp, *mq.CodeMessage) {
  28. pkg, err := svc.db2.Package().GetUserPackageByName(svc.db2.DefCtx(), msg.UserID, msg.BucketName, msg.PackageName)
  29. if err != nil {
  30. logger.WithField("UserID", msg.UserID).
  31. WithField("BucketName", msg.BucketName).
  32. WithField("PackageName", msg.PackageName).
  33. Warnf("get package by name: %s", err.Error())
  34. if errors.Is(err, gorm.ErrRecordNotFound) {
  35. return nil, mq.Failed(errorcode.DataNotFound, "package not found")
  36. }
  37. return nil, mq.Failed(errorcode.OperationFailed, "get package by name failed")
  38. }
  39. return mq.ReplyOK(coormq.NewGetPackageByNameResp(pkg))
  40. }
  41. func (svc *Service) CreatePackage(msg *coormq.CreatePackage) (*coormq.CreatePackageResp, *mq.CodeMessage) {
  42. var pkg cdssdk.Package
  43. err := svc.db2.DoTx(func(tx db2.SQLContext) error {
  44. var err error
  45. isAvai, _ := svc.db2.Bucket().IsAvailable(tx, msg.BucketID, msg.UserID)
  46. if !isAvai {
  47. return fmt.Errorf("bucket is not avaiable to the user")
  48. }
  49. pkg, err = svc.db2.Package().Create(tx, msg.BucketID, msg.Name)
  50. if err != nil {
  51. return fmt.Errorf("creating package: %w", err)
  52. }
  53. return nil
  54. })
  55. if err != nil {
  56. logger.WithField("BucketID", msg.BucketID).
  57. WithField("Name", msg.Name).
  58. Warn(err.Error())
  59. if errors.Is(err, gorm.ErrDuplicatedKey) {
  60. return nil, mq.Failed(errorcode.DataExists, "package already exists")
  61. }
  62. return nil, mq.Failed(errorcode.OperationFailed, err.Error())
  63. }
  64. svc.evtPub.Publish(&stgmod.BodyNewPackage{
  65. Info: pkg,
  66. })
  67. return mq.ReplyOK(coormq.NewCreatePackageResp(pkg))
  68. }
  69. func (svc *Service) UpdatePackage(msg *coormq.UpdatePackage) (*coormq.UpdatePackageResp, *mq.CodeMessage) {
  70. var added []cdssdk.Object
  71. err := svc.db2.DoTx(func(tx db2.SQLContext) error {
  72. _, err := svc.db2.Package().GetByID(tx, msg.PackageID)
  73. if err != nil {
  74. return fmt.Errorf("getting package by id: %w", err)
  75. }
  76. ad, err := svc.db2.Object().BatchAdd(tx, msg.PackageID, msg.Adds)
  77. if err != nil {
  78. return fmt.Errorf("adding objects: %w", err)
  79. }
  80. added = ad
  81. return nil
  82. })
  83. if err != nil {
  84. logger.WithField("PackageID", msg.PackageID).Warn(err.Error())
  85. return nil, mq.Failed(errorcode.OperationFailed, "update package failed")
  86. }
  87. addedMp := make(map[string]cdssdk.Object)
  88. for _, obj := range added {
  89. addedMp[obj.Path] = obj
  90. }
  91. for _, add := range msg.Adds {
  92. var blks []stgmod.BlockDistributionObjectInfo
  93. for _, stgID := range add.StorageIDs {
  94. blks = append(blks, stgmod.BlockDistributionObjectInfo{
  95. BlockType: stgmod.BlockTypeRaw,
  96. StorageID: stgID,
  97. })
  98. }
  99. svc.evtPub.Publish(&stgmod.BodyNewOrUpdateObject{
  100. Info: addedMp[add.Path],
  101. BlockDistribution: blks,
  102. })
  103. }
  104. return mq.ReplyOK(coormq.NewUpdatePackageResp(added))
  105. }
  106. func (svc *Service) DeletePackage(msg *coormq.DeletePackage) (*coormq.DeletePackageResp, *mq.CodeMessage) {
  107. err := svc.db2.DoTx(func(tx db2.SQLContext) error {
  108. isAvai, _ := svc.db2.Package().IsAvailable(tx, msg.UserID, msg.PackageID)
  109. if !isAvai {
  110. return fmt.Errorf("package is not available to the user")
  111. }
  112. err := svc.db2.Package().DeleteComplete(tx, msg.PackageID)
  113. if err != nil {
  114. return fmt.Errorf("deleting package: %w", err)
  115. }
  116. return nil
  117. })
  118. if err != nil {
  119. logger.WithField("UserID", msg.UserID).
  120. WithField("PackageID", msg.PackageID).
  121. Warnf(err.Error())
  122. return nil, mq.Failed(errorcode.OperationFailed, "delete package failed")
  123. }
  124. svc.evtPub.Publish(&stgmod.BodyPackageDeleted{
  125. PackageID: msg.PackageID,
  126. })
  127. return mq.ReplyOK(coormq.NewDeletePackageResp())
  128. }
  129. func (svc *Service) ClonePackage(msg *coormq.ClonePackage) (*coormq.ClonePackageResp, *mq.CodeMessage) {
  130. var pkg cdssdk.Package
  131. var oldObjIDs []cdssdk.ObjectID
  132. var newObjIDs []cdssdk.ObjectID
  133. err := svc.db2.DoTx(func(tx db2.SQLContext) error {
  134. var err error
  135. isAvai, _ := svc.db2.Bucket().IsAvailable(tx, msg.BucketID, msg.UserID)
  136. if !isAvai {
  137. return fmt.Errorf("bucket is not avaiable to the user")
  138. }
  139. pkg, err = svc.db2.Package().Create(tx, msg.BucketID, msg.Name)
  140. if err != nil {
  141. return fmt.Errorf("creating package: %w", err)
  142. }
  143. objs, err := svc.db2.Object().GetPackageObjects(tx, msg.PackageID)
  144. if err != nil {
  145. return fmt.Errorf("getting package objects: %w", err)
  146. }
  147. objBlks, err := svc.db2.ObjectBlock().GetInPackageID(tx, msg.PackageID)
  148. if err != nil {
  149. return fmt.Errorf("getting object blocks: %w", err)
  150. }
  151. clonedObjs := make([]cdssdk.Object, len(objs))
  152. for i, obj := range objs {
  153. clonedObjs[i] = obj
  154. clonedObjs[i].ObjectID = 0
  155. clonedObjs[i].PackageID = pkg.PackageID
  156. }
  157. err = svc.db2.Object().BatchCreate(tx, &clonedObjs)
  158. if err != nil {
  159. return fmt.Errorf("batch creating objects: %w", err)
  160. }
  161. oldToNew := make(map[cdssdk.ObjectID]cdssdk.ObjectID)
  162. for i, obj := range clonedObjs {
  163. oldToNew[objs[i].ObjectID] = obj.ObjectID
  164. oldObjIDs = append(oldObjIDs, objs[i].ObjectID)
  165. newObjIDs = append(newObjIDs, obj.ObjectID)
  166. }
  167. clonedBlks := make([]stgmod.ObjectBlock, len(objBlks))
  168. for i, blk := range objBlks {
  169. clonedBlks[i] = blk
  170. clonedBlks[i].ObjectID = oldToNew[blk.ObjectID]
  171. }
  172. err = svc.db2.ObjectBlock().BatchCreate(tx, clonedBlks)
  173. if err != nil {
  174. return fmt.Errorf("batch creating object blocks: %w", err)
  175. }
  176. return nil
  177. })
  178. if err != nil {
  179. if errors.Is(err, gorm.ErrDuplicatedKey) {
  180. return nil, mq.Failed(errorcode.DataExists, "package already exists")
  181. }
  182. return nil, mq.Failed(errorcode.OperationFailed, err.Error())
  183. }
  184. svc.evtPub.Publish(&stgmod.BodyPackageCloned{
  185. SourcePackageID: msg.PackageID,
  186. NewPackage: pkg,
  187. SourceObjectIDs: oldObjIDs,
  188. NewObjectIDs: newObjIDs,
  189. })
  190. return mq.ReplyOK(coormq.RespClonePackage(pkg))
  191. }
  192. func (svc *Service) GetPackageCachedStorages(msg *coormq.GetPackageCachedStorages) (*coormq.GetPackageCachedStoragesResp, *mq.CodeMessage) {
  193. isAva, err := svc.db2.Package().IsAvailable(svc.db2.DefCtx(), msg.UserID, msg.PackageID)
  194. if err != nil {
  195. logger.WithField("UserID", msg.UserID).
  196. WithField("PackageID", msg.PackageID).
  197. Warnf("check package available failed, err: %s", err.Error())
  198. return nil, mq.Failed(errorcode.OperationFailed, "check package available failed")
  199. }
  200. if !isAva {
  201. logger.WithField("UserID", msg.UserID).
  202. WithField("PackageID", msg.PackageID).
  203. Warnf("package is not available to the user")
  204. return nil, mq.Failed(errorcode.OperationFailed, "package is not available to the user")
  205. }
  206. // 这个函数只是统计哪些节点缓存了Package中的数据,不需要多么精确,所以可以不用事务
  207. objDetails, err := svc.db2.Object().GetPackageObjectDetails(svc.db2.DefCtx(), msg.PackageID)
  208. if err != nil {
  209. logger.WithField("PackageID", msg.PackageID).
  210. Warnf("get package block details: %s", err.Error())
  211. return nil, mq.Failed(errorcode.OperationFailed, "get package block details failed")
  212. }
  213. var packageSize int64
  214. stgInfoMap := make(map[cdssdk.StorageID]*cdssdk.StoragePackageCachingInfo)
  215. for _, obj := range objDetails {
  216. // 只要存了文件的一个块,就认为此节点存了整个文件
  217. for _, block := range obj.Blocks {
  218. info, ok := stgInfoMap[block.StorageID]
  219. if !ok {
  220. info = &cdssdk.StoragePackageCachingInfo{
  221. StorageID: block.StorageID,
  222. }
  223. stgInfoMap[block.StorageID] = info
  224. }
  225. info.FileSize += obj.Object.Size
  226. info.ObjectCount++
  227. }
  228. }
  229. var stgInfos []cdssdk.StoragePackageCachingInfo
  230. for _, stgInfo := range stgInfoMap {
  231. stgInfos = append(stgInfos, *stgInfo)
  232. }
  233. sort.Slice(stgInfos, func(i, j int) bool {
  234. return stgInfos[i].StorageID < stgInfos[j].StorageID
  235. })
  236. return mq.ReplyOK(coormq.ReqGetPackageCachedStoragesResp(stgInfos, packageSize))
  237. }
  238. func (svc *Service) AddAccessStat(msg *coormq.AddAccessStat) {
  239. pkgIDs := make([]cdssdk.PackageID, len(msg.Entries))
  240. objIDs := make([]cdssdk.ObjectID, len(msg.Entries))
  241. for i, e := range msg.Entries {
  242. pkgIDs[i] = e.PackageID
  243. objIDs[i] = e.ObjectID
  244. }
  245. err := svc.db2.DoTx(func(tx db2.SQLContext) error {
  246. avaiPkgIDs, err := svc.db2.Package().BatchTestPackageID(tx, pkgIDs)
  247. if err != nil {
  248. return fmt.Errorf("batch test package id: %w", err)
  249. }
  250. avaiObjIDs, err := svc.db2.Object().BatchTestObjectID(tx, objIDs)
  251. if err != nil {
  252. return fmt.Errorf("batch test object id: %w", err)
  253. }
  254. var willAdds []coormq.AddAccessStatEntry
  255. for _, e := range msg.Entries {
  256. if avaiPkgIDs[e.PackageID] && avaiObjIDs[e.ObjectID] {
  257. willAdds = append(willAdds, e)
  258. }
  259. }
  260. if len(willAdds) > 0 {
  261. err := svc.db2.PackageAccessStat().BatchAddCounter(tx, willAdds)
  262. if err != nil {
  263. return fmt.Errorf("batch add package access stat counter: %w", err)
  264. }
  265. err = svc.db2.ObjectAccessStat().BatchAddCounter(tx, willAdds)
  266. if err != nil {
  267. return fmt.Errorf("batch add object access stat counter: %w", err)
  268. }
  269. }
  270. return nil
  271. })
  272. if err != nil {
  273. logger.Warn(err.Error())
  274. }
  275. }

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