| @@ -7,6 +7,7 @@ import ( | |||
| "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2/ops2" | |||
| "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2/parser/state" | |||
| "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/factory" | |||
| "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/types" | |||
| cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types" | |||
| ) | |||
| @@ -79,7 +80,7 @@ func UseECMultiplier(ctx *state.GenerateState) { | |||
| } | |||
| bwNodes = append(bwNodes, swNode) | |||
| } | |||
| _, err := factory.GetBuilder(&to.Space).CreateECMultiplier() | |||
| _, err := factory.GetBuilder(&to.Space).CreateECMultiplier(true) | |||
| if err != nil { | |||
| return true | |||
| } | |||
| @@ -98,7 +99,12 @@ func UseECMultiplier(ctx *state.GenerateState) { | |||
| return true | |||
| } | |||
| if !factory.GetBuilder(&brNode.UserSpace).FeatureDesc().HasBypassHTTPRead { | |||
| store, err := factory.GetBuilder(&brNode.UserSpace).CreateShardStore(true) | |||
| if err != nil { | |||
| return true | |||
| } | |||
| _, ok = store.(types.HTTPShardRead) | |||
| if !ok { | |||
| return true | |||
| } | |||
| @@ -34,7 +34,7 @@ func UseMultipartUploadToShardStore(ctx *state.GenerateState) { | |||
| } | |||
| // Join的目的地必须支持MultipartUpload功能才能替换成分片上传 | |||
| multiUpload, err := factory.GetBuilder(&bwNode.UserSpace).CreateMultiparter() | |||
| multiUpload, err := factory.GetBuilder(&bwNode.UserSpace).CreateMultiparter(true) | |||
| if err != nil { | |||
| return true | |||
| } | |||
| @@ -26,11 +26,7 @@ func UseS2STransfer(ctx *state.GenerateState) { | |||
| func s2sFromShardStore(ctx *state.GenerateState, fromShard *ioswitch2.FromShardStore, frNode ops2.FromNode) { | |||
| fromStgBld := factory.GetBuilder(&fromShard.UserSpace) | |||
| if !fromStgBld.FeatureDesc().HasBypassShardRead { | |||
| return | |||
| } | |||
| s2s, err := fromStgBld.CreateS2STransfer() | |||
| s2s, err := fromStgBld.CreateS2STransfer(true) | |||
| if err != nil { | |||
| return | |||
| } | |||
| @@ -50,13 +46,7 @@ loop: | |||
| switch dstNode := dstNode.(type) { | |||
| case *ops2.BaseWriteNode: | |||
| dstStgBld := factory.GetBuilder(&dstNode.UserSpace) | |||
| if !dstStgBld.FeatureDesc().HasBypassBaseWrite { | |||
| failed = true | |||
| break | |||
| } | |||
| if !s2s.CanTransfer(&dstNode.UserSpace) { | |||
| if !s2s.CanTransfer(&fromShard.UserSpace, &dstNode.UserSpace) { | |||
| failed = true | |||
| break | |||
| } | |||
| @@ -99,11 +89,7 @@ loop: | |||
| func s2sFromBaseStore(ctx *state.GenerateState, fromBase *ioswitch2.FromBaseStore, frNode ops2.FromNode) { | |||
| fromStgBld := factory.GetBuilder(&fromBase.UserSpace) | |||
| if !fromStgBld.FeatureDesc().HasBypassBaseRead { | |||
| return | |||
| } | |||
| s2s, err := fromStgBld.CreateS2STransfer() | |||
| s2s, err := fromStgBld.CreateS2STransfer(true) | |||
| if err != nil { | |||
| return | |||
| } | |||
| @@ -123,13 +109,7 @@ loop: | |||
| switch dstNode := dstNode.(type) { | |||
| case *ops2.BaseWriteNode: | |||
| dstStgBld := factory.GetBuilder(&dstNode.UserSpace) | |||
| if !dstStgBld.FeatureDesc().HasBypassBaseWrite { | |||
| failed = true | |||
| break | |||
| } | |||
| if !s2s.CanTransfer(&dstNode.UserSpace) { | |||
| if !s2s.CanTransfer(&fromBase.UserSpace, &dstNode.UserSpace) { | |||
| failed = true | |||
| break | |||
| } | |||
| @@ -91,7 +91,11 @@ func (b *builder) getToken() (string, error) { | |||
| return "", fmt.Errorf("clusterID %s not found", stgType.ClusterID) | |||
| } | |||
| func (b *builder) CreateECMultiplier() (types.ECMultiplier, error) { | |||
| func (b *builder) CreateECMultiplier(typeOnly bool) (types.ECMultiplier, error) { | |||
| if typeOnly { | |||
| return (*ECMultiplier)(nil), nil | |||
| } | |||
| feat := utils.FindFeature[*cortypes.ECMultiplierFeature](b.detail) | |||
| if feat == nil { | |||
| return nil, fmt.Errorf("feature ECMultiplier not found") | |||
| @@ -24,13 +24,14 @@ type builder struct { | |||
| } | |||
| func (b *builder) FeatureDesc() types.FeatureDesc { | |||
| return types.FeatureDesc{ | |||
| HasBypassShardWrite: true, | |||
| HasBypassShardRead: true, | |||
| } | |||
| return types.FeatureDesc{} | |||
| } | |||
| func (b *builder) CreateShardStore() (types.ShardStore, error) { | |||
| func (b *builder) CreateShardStore(typeOnly bool) (types.ShardStore, error) { | |||
| if typeOnly { | |||
| return (*ShardStore)(nil), nil | |||
| } | |||
| cred, ok := b.detail.UserSpace.Credential.(*cortypes.LocalCred) | |||
| if !ok { | |||
| return nil, fmt.Errorf("invalid storage credential type %T for local storage", b.detail.UserSpace.Credential) | |||
| @@ -39,7 +40,11 @@ func (b *builder) CreateShardStore() (types.ShardStore, error) { | |||
| return NewShardStore(cred.RootDir, b.detail) | |||
| } | |||
| func (b *builder) CreateBaseStore() (types.BaseStore, error) { | |||
| func (b *builder) CreateBaseStore(typeOnly bool) (types.BaseStore, error) { | |||
| if typeOnly { | |||
| return (*BaseStore)(nil), nil | |||
| } | |||
| cred, ok := b.detail.UserSpace.Credential.(*cortypes.LocalCred) | |||
| if !ok { | |||
| return nil, fmt.Errorf("invalid storage credential type %T for local storage", b.detail.UserSpace.Credential) | |||
| @@ -48,7 +53,11 @@ func (b *builder) CreateBaseStore() (types.BaseStore, error) { | |||
| return NewBaseStore(cred.RootDir, b.detail) | |||
| } | |||
| func (b *builder) CreateMultiparter() (types.Multiparter, error) { | |||
| func (b *builder) CreateMultiparter(typeOnly bool) (types.Multiparter, error) { | |||
| if typeOnly { | |||
| return (*Multiparter)(nil), nil | |||
| } | |||
| feat := utils.FindFeature[*cortypes.MultipartUploadFeature](b.detail) | |||
| if feat == nil { | |||
| return nil, fmt.Errorf("feature %T not found", cortypes.MultipartUploadFeature{}) | |||
| @@ -59,7 +68,11 @@ func (b *builder) CreateMultiparter() (types.Multiparter, error) { | |||
| }, nil | |||
| } | |||
| func (b *builder) CreateS2STransfer() (types.S2STransfer, error) { | |||
| func (b *builder) CreateS2STransfer(typeOnly bool) (types.S2STransfer, error) { | |||
| if typeOnly { | |||
| return (*S2STransfer)(nil), nil | |||
| } | |||
| feat := utils.FindFeature[*cortypes.S2STransferFeature](b.detail) | |||
| if feat == nil { | |||
| return nil, fmt.Errorf("feature %T not found", cortypes.S2STransferFeature{}) | |||
| @@ -17,13 +17,13 @@ type S2STransfer struct { | |||
| } | |||
| // 只有同一个机器的存储之间才可以进行数据直传 | |||
| func (s *S2STransfer) CanTransfer(src *clitypes.UserSpaceDetail) bool { | |||
| func (*S2STransfer) CanTransfer(src, dst *clitypes.UserSpaceDetail) bool { | |||
| _, ok := src.UserSpace.Storage.(*cortypes.LocalType) | |||
| if !ok { | |||
| return false | |||
| } | |||
| if src.RecommendHub != s.detail.RecommendHub { | |||
| if src.RecommendHub.HubID != dst.RecommendHub.HubID { | |||
| return false | |||
| } | |||
| @@ -30,16 +30,14 @@ func newBuilder(detail *clitypes.UserSpaceDetail) types.StorageBuilder { | |||
| } | |||
| func (b *builder) FeatureDesc() types.FeatureDesc { | |||
| return types.FeatureDesc{ | |||
| HasBypassShardWrite: true, | |||
| HasBypassBaseWrite: true, | |||
| HasBypassShardRead: true, | |||
| HasBypassBaseRead: true, | |||
| HasBypassHTTPRead: true, | |||
| } | |||
| return types.FeatureDesc{} | |||
| } | |||
| func (b *builder) CreateShardStore() (types.ShardStore, error) { | |||
| func (b *builder) CreateShardStore(typeOnly bool) (types.ShardStore, error) { | |||
| if typeOnly { | |||
| return (*ShardStore)(nil), nil | |||
| } | |||
| stgType := b.detail.UserSpace.Storage.(*cortypes.OBSType) | |||
| cred, ok := b.detail.UserSpace.Credential.(*cortypes.OBSCred) | |||
| if !ok { | |||
| @@ -54,7 +52,11 @@ func (b *builder) CreateShardStore() (types.ShardStore, error) { | |||
| return NewShardStore(b.detail, stgType, cred, cli, bucket) | |||
| } | |||
| func (b *builder) CreateBaseStore() (types.BaseStore, error) { | |||
| func (b *builder) CreateBaseStore(typeOnly bool) (types.BaseStore, error) { | |||
| if typeOnly { | |||
| return (*s3stg.BaseStore)(nil), nil | |||
| } | |||
| stgType := b.detail.UserSpace.Storage.(*cortypes.OBSType) | |||
| cred, ok := b.detail.UserSpace.Credential.(*cortypes.OBSCred) | |||
| if !ok { | |||
| @@ -88,7 +90,11 @@ func createClient(stgType *cortypes.OBSType, cred *cortypes.OBSCred) (*s3.Client | |||
| return cli, stgType.Bucket, nil | |||
| } | |||
| func (b *builder) CreateMultiparter() (types.Multiparter, error) { | |||
| func (b *builder) CreateMultiparter(typeOnly bool) (types.Multiparter, error) { | |||
| if typeOnly { | |||
| return (*s3stg.Multiparter)(nil), nil | |||
| } | |||
| stgType := b.detail.UserSpace.Storage.(*cortypes.OBSType) | |||
| feat := utils.FindFeature[*cortypes.MultipartUploadFeature](b.detail) | |||
| if feat == nil { | |||
| @@ -113,7 +119,11 @@ func (b *builder) CreateMultiparter() (types.Multiparter, error) { | |||
| ), nil | |||
| } | |||
| func (b *builder) CreateS2STransfer() (types.S2STransfer, error) { | |||
| func (b *builder) CreateS2STransfer(typeOnly bool) (types.S2STransfer, error) { | |||
| if typeOnly { | |||
| return (*S2STransfer)(nil), nil | |||
| } | |||
| stgType := b.detail.UserSpace.Storage.(*cortypes.OBSType) | |||
| feat := utils.FindFeature[*cortypes.S2STransferFeature](b.detail) | |||
| if feat == nil { | |||
| @@ -36,14 +36,14 @@ func NewS2STransfer(stgType *cortypes.OBSType, cred *cortypes.OBSCred, feat *cor | |||
| } | |||
| // 判断是否能从指定的源存储中直传到当前存储的目的路径 | |||
| func (s *S2STransfer) CanTransfer(src *clitypes.UserSpaceDetail) bool { | |||
| req := s.makeRequest(src, "") | |||
| func (*S2STransfer) CanTransfer(src, dst *clitypes.UserSpaceDetail) bool { | |||
| req := makeRequest(src, "") | |||
| return req != nil | |||
| } | |||
| // 执行数据直传。返回传输后的文件路径 | |||
| func (s *S2STransfer) Transfer(ctx context.Context, src *clitypes.UserSpaceDetail, srcPath string, dstPath string) (types.FileInfo, error) { | |||
| req := s.makeRequest(src, srcPath) | |||
| req := makeRequest(src, srcPath) | |||
| if req == nil { | |||
| return types.FileInfo{}, fmt.Errorf("unsupported source storage type: %T", src.UserSpace.Storage) | |||
| } | |||
| @@ -122,30 +122,6 @@ func (s *S2STransfer) Transfer(ctx context.Context, src *clitypes.UserSpaceDetai | |||
| }, nil | |||
| } | |||
| func (s *S2STransfer) makeRequest(srcStg *clitypes.UserSpaceDetail, srcPath string) *model.SrcNodeReq { | |||
| switch srcType := srcStg.UserSpace.Storage.(type) { | |||
| case *cortypes.OBSType: | |||
| cloudType := "HuaweiCloud" | |||
| cred, ok := srcStg.UserSpace.Credential.(*cortypes.OBSCred) | |||
| if !ok { | |||
| return nil | |||
| } | |||
| return &model.SrcNodeReq{ | |||
| CloudType: &cloudType, | |||
| Region: &srcType.Region, | |||
| Ak: &cred.AK, | |||
| Sk: &cred.SK, | |||
| Bucket: &srcType.Bucket, | |||
| ObjectKey: &[]string{srcPath}, | |||
| } | |||
| default: | |||
| return nil | |||
| } | |||
| } | |||
| func (s *S2STransfer) waitTask(ctx context.Context, taskId int64) (int64, error) { | |||
| ticker := time.NewTicker(time.Second * 5) | |||
| defer ticker.Stop() | |||
| @@ -198,3 +174,27 @@ func (s *S2STransfer) Close() { | |||
| }) | |||
| } | |||
| } | |||
| func makeRequest(srcStg *clitypes.UserSpaceDetail, srcPath string) *model.SrcNodeReq { | |||
| switch srcType := srcStg.UserSpace.Storage.(type) { | |||
| case *cortypes.OBSType: | |||
| cloudType := "HuaweiCloud" | |||
| cred, ok := srcStg.UserSpace.Credential.(*cortypes.OBSCred) | |||
| if !ok { | |||
| return nil | |||
| } | |||
| return &model.SrcNodeReq{ | |||
| CloudType: &cloudType, | |||
| Region: &srcType.Region, | |||
| Ak: &cred.AK, | |||
| Sk: &cred.SK, | |||
| Bucket: &srcType.Bucket, | |||
| ObjectKey: &[]string{srcPath}, | |||
| } | |||
| default: | |||
| return nil | |||
| } | |||
| } | |||
| @@ -64,7 +64,7 @@ func (p *Pool) GetShardStore(spaceDetail *clitypes.UserSpaceDetail) (types.Shard | |||
| if space.store == nil { | |||
| bld := factory.GetBuilder(spaceDetail) | |||
| store, err := bld.CreateShardStore() | |||
| store, err := bld.CreateShardStore(false) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| @@ -76,17 +76,17 @@ func (p *Pool) GetShardStore(spaceDetail *clitypes.UserSpaceDetail) (types.Shard | |||
| } | |||
| func (p *Pool) GetBaseStore(spaceDetail *clitypes.UserSpaceDetail) (types.BaseStore, error) { | |||
| return factory.GetBuilder(spaceDetail).CreateBaseStore() | |||
| return factory.GetBuilder(spaceDetail).CreateBaseStore(false) | |||
| } | |||
| func (p *Pool) GetMultiparter(spaceDetail *clitypes.UserSpaceDetail) (types.Multiparter, error) { | |||
| return factory.GetBuilder(spaceDetail).CreateMultiparter() | |||
| return factory.GetBuilder(spaceDetail).CreateMultiparter(false) | |||
| } | |||
| func (p *Pool) GetS2STransfer(spaceDetail *clitypes.UserSpaceDetail) (types.S2STransfer, error) { | |||
| return factory.GetBuilder(spaceDetail).CreateS2STransfer() | |||
| return factory.GetBuilder(spaceDetail).CreateS2STransfer(false) | |||
| } | |||
| func (p *Pool) GetECMultiplier(spaceDetail *clitypes.UserSpaceDetail) (types.ECMultiplier, error) { | |||
| return factory.GetBuilder(spaceDetail).CreateECMultiplier() | |||
| return factory.GetBuilder(spaceDetail).CreateECMultiplier(false) | |||
| } | |||
| @@ -29,16 +29,14 @@ func newBuilder(detail *clitypes.UserSpaceDetail) types.StorageBuilder { | |||
| } | |||
| func (b *builder) FeatureDesc() types.FeatureDesc { | |||
| return types.FeatureDesc{ | |||
| HasBypassShardWrite: true, | |||
| HasBypassBaseWrite: true, | |||
| HasBypassShardRead: true, | |||
| HasBypassBaseRead: true, | |||
| HasBypassHTTPRead: false, | |||
| } | |||
| return types.FeatureDesc{} | |||
| } | |||
| func (b *builder) CreateShardStore() (types.ShardStore, error) { | |||
| func (b *builder) CreateShardStore(typeOnly bool) (types.ShardStore, error) { | |||
| if typeOnly { | |||
| return (*ShardStore)(nil), nil | |||
| } | |||
| stgType := b.detail.UserSpace.Storage.(*cortypes.S3Type) | |||
| s3Cred, ok := b.detail.UserSpace.Credential.(*cortypes.S3Cred) | |||
| if !ok { | |||
| @@ -53,7 +51,11 @@ func (b *builder) CreateShardStore() (types.ShardStore, error) { | |||
| return NewShardStore(b.detail, cli, bkt, ShardStoreOption{UseAWSSha256: true}) | |||
| } | |||
| func (b *builder) CreateBaseStore() (types.BaseStore, error) { | |||
| func (b *builder) CreateBaseStore(typeOnly bool) (types.BaseStore, error) { | |||
| if typeOnly { | |||
| return (*BaseStore)(nil), nil | |||
| } | |||
| stgType := b.detail.UserSpace.Storage.(*cortypes.S3Type) | |||
| s3Cred, ok := b.detail.UserSpace.Credential.(*cortypes.S3Cred) | |||
| if !ok { | |||
| @@ -89,7 +91,11 @@ func createClient(stgType *cortypes.S3Type, cred *cortypes.S3Cred) (*s3.Client, | |||
| return cli, stgType.Bucket, nil | |||
| } | |||
| func (b *builder) CreateMultiparter() (types.Multiparter, error) { | |||
| func (b *builder) CreateMultiparter(typeOnly bool) (types.Multiparter, error) { | |||
| if typeOnly { | |||
| return (*Multiparter)(nil), nil | |||
| } | |||
| stgType := b.detail.UserSpace.Storage.(*cortypes.S3Type) | |||
| feat := utils.FindFeature[*cortypes.MultipartUploadFeature](b.detail) | |||
| if feat == nil { | |||
| @@ -14,22 +14,22 @@ func (b *EmptyBuilder) FeatureDesc() FeatureDesc { | |||
| return FeatureDesc{} | |||
| } | |||
| func (b *EmptyBuilder) CreateShardStore() (ShardStore, error) { | |||
| func (b *EmptyBuilder) CreateShardStore(typeOnly bool) (ShardStore, error) { | |||
| return nil, fmt.Errorf("create shard store for %T: %w", b.Detail.UserSpace.Storage, ErrUnsupported) | |||
| } | |||
| func (b *EmptyBuilder) CreateBaseStore() (BaseStore, error) { | |||
| func (b *EmptyBuilder) CreateBaseStore(typeOnly bool) (BaseStore, error) { | |||
| return nil, fmt.Errorf("create base store for %T: %w", b.Detail.UserSpace.Storage, ErrUnsupported) | |||
| } | |||
| func (b *EmptyBuilder) CreateMultiparter() (Multiparter, error) { | |||
| func (b *EmptyBuilder) CreateMultiparter(typeOnly bool) (Multiparter, error) { | |||
| return nil, fmt.Errorf("create multipart initiator for %T: %w", b.Detail.UserSpace.Storage, ErrUnsupported) | |||
| } | |||
| func (b *EmptyBuilder) CreateS2STransfer() (S2STransfer, error) { | |||
| func (b *EmptyBuilder) CreateS2STransfer(typeOnly bool) (S2STransfer, error) { | |||
| return nil, fmt.Errorf("create s2s transfer for %T: %w", b.Detail.UserSpace.Storage, ErrUnsupported) | |||
| } | |||
| func (b *EmptyBuilder) CreateECMultiplier() (ECMultiplier, error) { | |||
| func (b *EmptyBuilder) CreateECMultiplier(typeOnly bool) (ECMultiplier, error) { | |||
| return nil, fmt.Errorf("create ec multiplier for %T: %w", b.Detail.UserSpace.Storage, ErrUnsupported) | |||
| } | |||
| @@ -7,8 +7,8 @@ import ( | |||
| ) | |||
| type S2STransfer interface { | |||
| // 判断是否能从指定的源存储中直传到当前存储的目的路径。仅在生成计划时使用 | |||
| CanTransfer(src *clitypes.UserSpaceDetail) bool | |||
| // 【静态方法】判断是否能从指定的源存储中直传到当前存储的目的路径。仅在生成计划时使用 | |||
| CanTransfer(src, dst *clitypes.UserSpaceDetail) bool | |||
| // 从远端获取文件并保存到本地路径 | |||
| Transfer(ctx context.Context, src *clitypes.UserSpaceDetail, srcPath string, dstPath string) (FileInfo, error) | |||
| Close() | |||
| @@ -6,7 +6,9 @@ import ( | |||
| ) | |||
| type Multiparter interface { | |||
| // 【静态方法】 | |||
| MaxPartSize() int64 | |||
| // 【静态方法】 | |||
| MinPartSize() int64 | |||
| // 启动一个分片上传 | |||
| Initiate(ctx context.Context) (MultipartTask, error) | |||
| @@ -18,32 +18,24 @@ type StorageEvent interface{} | |||
| type StorageEventChan = async.UnboundChannel[StorageEvent] | |||
| // 创建存储系统各种组件。 | |||
| // | |||
| // typeOnly参数为true时仅创建组件类型(带类型的nil值),不创建组件实例。 | |||
| type StorageBuilder interface { | |||
| // 关于此存储系统特性功能的描述 | |||
| FeatureDesc() FeatureDesc | |||
| // 创建一个提供基础读写存储服务能力的组件 | |||
| CreateBaseStore() (BaseStore, error) | |||
| CreateBaseStore(typeOnly bool) (BaseStore, error) | |||
| // 创建一个分片存储组件 | |||
| CreateShardStore() (ShardStore, error) | |||
| CreateShardStore(typeOnly bool) (ShardStore, error) | |||
| // 创建一个分片上传组件 | |||
| CreateMultiparter() (Multiparter, error) | |||
| CreateMultiparter(typeOnly bool) (Multiparter, error) | |||
| // 创建一个存储服务直传组件 | |||
| CreateS2STransfer() (S2STransfer, error) | |||
| CreateECMultiplier() (ECMultiplier, error) | |||
| CreateS2STransfer(typeOnly bool) (S2STransfer, error) | |||
| CreateECMultiplier(typeOnly bool) (ECMultiplier, error) | |||
| } | |||
| type FeatureDesc struct { | |||
| // 是否能旁路上传分片 | |||
| HasBypassShardWrite bool | |||
| // 是否能旁路上传公共存储 | |||
| HasBypassBaseWrite bool | |||
| // 是否能旁路读取分片 | |||
| HasBypassShardRead bool | |||
| // 公共存储是否支持旁路读取 | |||
| HasBypassBaseRead bool | |||
| // 是否能通过HTTP读取 | |||
| HasBypassHTTPRead bool | |||
| } | |||
| type FeatureDesc struct{} | |||
| type FileInfo struct { | |||
| // 分片在存储系统中的路径,可以通过BaseStore读取的 | |||