| @@ -43,6 +43,7 @@ func init() { | |||||
| cmd.Flags().StringVarP(&opt.HTTPListenAddr, "listen", "", "", "http listen address, will override config file") | cmd.Flags().StringVarP(&opt.HTTPListenAddr, "listen", "", "", "http listen address, will override config file") | ||||
| cmd.Flags().BoolVarP(&opt.DisableMount, "no-mount", "", false, "disable mount") | cmd.Flags().BoolVarP(&opt.DisableMount, "no-mount", "", false, "disable mount") | ||||
| cmd.Flags().StringVarP(&opt.MountPoint, "mount", "", "", "mount point, will override config file") | cmd.Flags().StringVarP(&opt.MountPoint, "mount", "", "", "mount point, will override config file") | ||||
| cmd.Flags().BoolVarP(&opt.Standalone, "standalone", "", false, "standalone mode") | |||||
| RootCmd.AddCommand(&cmd) | RootCmd.AddCommand(&cmd) | ||||
| } | } | ||||
| @@ -51,6 +52,7 @@ type serveHTTPOptions struct { | |||||
| HTTPListenAddr string | HTTPListenAddr string | ||||
| DisableMount bool | DisableMount bool | ||||
| MountPoint string | MountPoint string | ||||
| Standalone bool | |||||
| } | } | ||||
| func serveHTTP(configPath string, opts serveHTTPOptions) { | func serveHTTP(configPath string, opts serveHTTPOptions) { | ||||
| @@ -68,6 +70,7 @@ func serveHTTP(configPath string, opts serveHTTPOptions) { | |||||
| stgglb.InitLocal(config.Cfg().Local) | stgglb.InitLocal(config.Cfg().Local) | ||||
| stgglb.InitPools(&config.Cfg().HubRPC, &config.Cfg().CoordinatorRPC) | stgglb.InitPools(&config.Cfg().HubRPC, &config.Cfg().CoordinatorRPC) | ||||
| stgglb.StandaloneMode = opts.Standalone | |||||
| // 数据库 | // 数据库 | ||||
| db, err := db.NewDB(&config.Cfg().DB) | db, err := db.NewDB(&config.Cfg().DB) | ||||
| @@ -131,7 +131,12 @@ func (i *DownloadObjectIterator) downloadDirect(req downloadReqeust2, strg strat | |||||
| exeCtx := exec.NewExecContext() | exeCtx := exec.NewExecContext() | ||||
| exec.SetValueByType(exeCtx, i.downloader.stgPool) | exec.SetValueByType(exeCtx, i.downloader.stgPool) | ||||
| exec := plans.Execute(exeCtx) | exec := plans.Execute(exeCtx) | ||||
| go exec.Wait(context.TODO()) | |||||
| go func() { | |||||
| _, err := exec.Wait(context.TODO()) | |||||
| if err != nil { | |||||
| logger.Warnf("downloading object %v: %v", req.Raw.ObjectID, err) | |||||
| } | |||||
| }() | |||||
| return exec.BeginRead(strHandle) | return exec.BeginRead(strHandle) | ||||
| } | } | ||||
| @@ -62,34 +62,39 @@ func (s *UserSpaceMeta) load(keys []types.UserSpaceID) ([]types.UserSpaceDetail, | |||||
| return vs, oks | return vs, oks | ||||
| } | } | ||||
| coorCli := stgglb.CoordinatorRPCPool.Get() | |||||
| defer coorCli.Release() | |||||
| stgs := make([]cortypes.StorageType, len(spaces)) | |||||
| detailMap := make(map[types.UserSpaceID]*types.UserSpaceDetail) | |||||
| for i := range spaces { | for i := range spaces { | ||||
| stgs[i] = spaces[i].Storage | |||||
| detailMap[spaces[i].UserSpaceID] = &types.UserSpaceDetail{ | |||||
| UserID: stgglb.Local.UserID, | |||||
| UserSpace: spaces[i], | |||||
| } | |||||
| } | } | ||||
| selectHubs, cerr := coorCli.SelectStorageHub(context.Background(), &corrpc.SelectStorageHub{ | |||||
| Storages: stgs, | |||||
| }) | |||||
| if cerr != nil { | |||||
| logger.Warnf("get storage details: %v", cerr) | |||||
| return vs, oks | |||||
| } | |||||
| if !stgglb.StandaloneMode { | |||||
| coorCli := stgglb.CoordinatorRPCPool.Get() | |||||
| defer coorCli.Release() | |||||
| detailMap := make(map[types.UserSpaceID]types.UserSpaceDetail) | |||||
| for i := range spaces { | |||||
| detailMap[spaces[i].UserSpaceID] = types.UserSpaceDetail{ | |||||
| UserID: stgglb.Local.UserID, | |||||
| UserSpace: spaces[i], | |||||
| RecommendHub: selectHubs.Hubs[i], | |||||
| stgs := make([]cortypes.StorageType, len(spaces)) | |||||
| for i := range spaces { | |||||
| stgs[i] = spaces[i].Storage | |||||
| } | |||||
| selectHubs, cerr := coorCli.SelectStorageHub(context.Background(), &corrpc.SelectStorageHub{ | |||||
| Storages: stgs, | |||||
| }) | |||||
| if cerr != nil { | |||||
| logger.Warnf("get storage details: %v", cerr) | |||||
| return vs, oks | |||||
| } | |||||
| for i := range spaces { | |||||
| detailMap[spaces[i].UserSpaceID].RecommendHub = selectHubs.Hubs[i] | |||||
| } | } | ||||
| } | } | ||||
| for i := range keys { | for i := range keys { | ||||
| if detail, ok := detailMap[keys[i]]; ok { | if detail, ok := detailMap[keys[i]]; ok { | ||||
| vs[i] = detail | |||||
| vs[i] = *detail | |||||
| oks[i] = true | oks[i] = true | ||||
| } | } | ||||
| } | } | ||||
| @@ -9,6 +9,15 @@ func init() { | |||||
| } | } | ||||
| RootCmd.AddCommand(ttCmd) | RootCmd.AddCommand(ttCmd) | ||||
| lsCmd := &cobra.Command{ | |||||
| Use: "ls", | |||||
| Short: "list all jobs", | |||||
| Run: func(cmd *cobra.Command, args []string) { | |||||
| tickTockLs(GetCmdCtx(cmd)) | |||||
| }, | |||||
| } | |||||
| ttCmd.AddCommand(lsCmd) | |||||
| runCmd := &cobra.Command{ | runCmd := &cobra.Command{ | ||||
| Use: "run [jobName]", | Use: "run [jobName]", | ||||
| Short: "run job now", | Short: "run job now", | ||||
| @@ -20,6 +29,13 @@ func init() { | |||||
| ttCmd.AddCommand(runCmd) | ttCmd.AddCommand(runCmd) | ||||
| } | } | ||||
| func tickTockLs(ctx *CommandContext) { | |||||
| names := ctx.repl.tktk.GetJobNames() | |||||
| for _, name := range names { | |||||
| println(name) | |||||
| } | |||||
| } | |||||
| func tickTockRun(ctx *CommandContext, jobName string) { | func tickTockRun(ctx *CommandContext, jobName string) { | ||||
| ctx.repl.tktk.RunNow(jobName) | ctx.repl.tktk.RunNow(jobName) | ||||
| } | } | ||||
| @@ -255,12 +255,12 @@ func (svc *ObjectService) Download(req downloader.DownloadReqeust) (*downloader. | |||||
| // 初始化下载过程 | // 初始化下载过程 | ||||
| downloading, err := iter.MoveNext() | downloading, err := iter.MoveNext() | ||||
| if downloading == nil { | |||||
| return nil, fmt.Errorf("object %v not found", req.ObjectID) | |||||
| } | |||||
| if err != nil { | if err != nil { | ||||
| return nil, err | return nil, err | ||||
| } | } | ||||
| if downloading.Object == nil { | |||||
| return nil, fmt.Errorf("object %v not found", req.ObjectID) | |||||
| } | |||||
| return downloading, nil | return downloading, nil | ||||
| } | } | ||||
| @@ -4,6 +4,7 @@ import ( | |||||
| "fmt" | "fmt" | ||||
| "github.com/go-co-op/gocron/v2" | "github.com/go-co-op/gocron/v2" | ||||
| "github.com/samber/lo" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/logger" | "gitlink.org.cn/cloudream/common/pkgs/logger" | ||||
| "gitlink.org.cn/cloudream/jcs-pub/client/internal/db" | "gitlink.org.cn/cloudream/jcs-pub/client/internal/db" | ||||
| "gitlink.org.cn/cloudream/jcs-pub/client/internal/metacache" | "gitlink.org.cn/cloudream/jcs-pub/client/internal/metacache" | ||||
| @@ -57,6 +58,10 @@ func (t *TickTock) Stop() { | |||||
| t.sch.Shutdown() | t.sch.Shutdown() | ||||
| } | } | ||||
| func (t *TickTock) GetJobNames() []string { | |||||
| return lo.Keys(t.jobs) | |||||
| } | |||||
| func (t *TickTock) RunNow(jobName string) { | func (t *TickTock) RunNow(jobName string) { | ||||
| j, ok := t.jobs[jobName] | j, ok := t.jobs[jobName] | ||||
| if !ok { | if !ok { | ||||
| @@ -82,7 +82,7 @@ type UserSpace struct { | |||||
| ShardStore *cotypes.ShardStoreUserConfig `gorm:"column:ShardStore; type:json; serializer:json" json:"shardStore"` | ShardStore *cotypes.ShardStoreUserConfig `gorm:"column:ShardStore; type:json; serializer:json" json:"shardStore"` | ||||
| // 存储服务特性功能的配置 | // 存储服务特性功能的配置 | ||||
| Features []cotypes.StorageFeature `json:"features" gorm:"column:Features; type:json; serializer:union"` | Features []cotypes.StorageFeature `json:"features" gorm:"column:Features; type:json; serializer:union"` | ||||
| // 各种组件保存数据的根目录。组件工作过程中都会以这个目录为根。 | |||||
| // 各种组件保存数据的根目录。组件工作过程中都会以这个目录为根(除了BaseStore)。 | |||||
| WorkingDir string `gorm:"column:WorkingDir; type:varchar(1024); not null" json:"workingDir"` | WorkingDir string `gorm:"column:WorkingDir; type:varchar(1024); not null" json:"workingDir"` | ||||
| // 工作目录在存储系统中的真实路径。当工作路径在挂载点内时,这个字段记录的是挂载背后的真实路径。部分直接与存储系统交互的组件需要知道真实路径。 | // 工作目录在存储系统中的真实路径。当工作路径在挂载点内时,这个字段记录的是挂载背后的真实路径。部分直接与存储系统交互的组件需要知道真实路径。 | ||||
| // RealWorkingDir string `gorm:"column:RealWorkingDir; type:varchar(1024); not null" json:"realWorkingDir"` | // RealWorkingDir string `gorm:"column:RealWorkingDir; type:varchar(1024); not null" json:"realWorkingDir"` | ||||
| @@ -18,6 +18,7 @@ import ( | |||||
| func init() { | func init() { | ||||
| exec.UseOp[*BaseWrite]() | exec.UseOp[*BaseWrite]() | ||||
| exec.UseOp[*BaseRead]() | exec.UseOp[*BaseRead]() | ||||
| exec.UseOp[*BaseReadDyn]() | |||||
| } | } | ||||
| type BaseRead struct { | type BaseRead struct { | ||||
| @@ -62,7 +63,7 @@ func (o *BaseRead) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| } | } | ||||
| func (o *BaseRead) String() string { | func (o *BaseRead) String() string { | ||||
| return fmt.Sprintf("PublicRead %v:%v -> %v", o.UserSpace, o.Path, o.Output) | |||||
| return fmt.Sprintf("BaseRead(opt=%v) %v:%v -> %v", o.Option, o.UserSpace, o.Path, o.Output) | |||||
| } | } | ||||
| type BaseReadDyn struct { | type BaseReadDyn struct { | ||||
| @@ -97,6 +98,7 @@ func (o *BaseReadDyn) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| stream, err := store.Read(info.Path, o.Option) | stream, err := store.Read(info.Path, o.Option) | ||||
| if err != nil { | if err != nil { | ||||
| logger.Warnf("reading file %v: %v", info.Path, err) | |||||
| return fmt.Errorf("reading object %v: %w", o.FileInfo, err) | return fmt.Errorf("reading object %v: %w", o.FileInfo, err) | ||||
| } | } | ||||
| @@ -112,7 +114,7 @@ func (o *BaseReadDyn) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| } | } | ||||
| func (o *BaseReadDyn) String() string { | func (o *BaseReadDyn) String() string { | ||||
| return fmt.Sprintf("BaseReadDyn %v:%v -> %v", o.UserSpace, o.FileInfo, o.Output) | |||||
| return fmt.Sprintf("BaseReadDyn(opt=%v) %v:%v -> %v", o.Option, o.UserSpace, o.FileInfo, o.Output) | |||||
| } | } | ||||
| type BaseWrite struct { | type BaseWrite struct { | ||||
| @@ -156,7 +158,7 @@ func (o *BaseWrite) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| } | } | ||||
| func (o *BaseWrite) String() string { | func (o *BaseWrite) String() string { | ||||
| return fmt.Sprintf("PublicWrite %v -> %v:%v", o.Input, o.UserSpace, o.Path) | |||||
| return fmt.Sprintf("BaseWrite %v -> %v:%v, %v", o.Input, o.UserSpace, o.Path, o.FileInfo) | |||||
| } | } | ||||
| type BaseReadNode struct { | type BaseReadNode struct { | ||||
| @@ -9,7 +9,6 @@ import ( | |||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/utils" | "gitlink.org.cn/cloudream/common/pkgs/ioswitch/utils" | ||||
| "gitlink.org.cn/cloudream/common/utils/io2" | "gitlink.org.cn/cloudream/common/utils/io2" | ||||
| "gitlink.org.cn/cloudream/common/utils/math2" | |||||
| "gitlink.org.cn/cloudream/common/utils/sync2" | "gitlink.org.cn/cloudream/common/utils/sync2" | ||||
| clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types" | clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types" | ||||
| "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ec" | "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ec" | ||||
| @@ -50,9 +49,11 @@ func (o *ECMultiply) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| outputWrs[i] = wr | outputWrs[i] = wr | ||||
| } | } | ||||
| /// !!! 缓冲区大小必须是ChunkSize大小,因为Chunk数据很有可能来自于一个被Split的完整文件,此时必须保证按顺序读取每一个Chunk的数据 !!! | |||||
| inputChunks := make([][]byte, len(o.Inputs)) | inputChunks := make([][]byte, len(o.Inputs)) | ||||
| for i := range o.Inputs { | for i := range o.Inputs { | ||||
| inputChunks[i] = make([]byte, math2.Min(o.ChunkSize, 64*1024)) | |||||
| inputChunks[i] = make([]byte, o.ChunkSize) | |||||
| } | } | ||||
| // 输出用两个缓冲轮换 | // 输出用两个缓冲轮换 | ||||
| @@ -60,7 +61,7 @@ func (o *ECMultiply) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| for i := 0; i < 2; i++ { | for i := 0; i < 2; i++ { | ||||
| outputChunks := make([][]byte, len(o.Outputs)) | outputChunks := make([][]byte, len(o.Outputs)) | ||||
| for i := range o.Outputs { | for i := range o.Outputs { | ||||
| outputChunks[i] = make([]byte, math2.Min(o.ChunkSize, 64*1024)) | |||||
| outputChunks[i] = make([]byte, o.ChunkSize) | |||||
| } | } | ||||
| outputBufPool.PutEmpty(outputChunks) | outputBufPool.PutEmpty(outputChunks) | ||||
| } | } | ||||
| @@ -69,22 +70,12 @@ func (o *ECMultiply) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| go func() { | go func() { | ||||
| mul := ec.GaloisMultiplier().BuildGalois() | mul := ec.GaloisMultiplier().BuildGalois() | ||||
| defer outputBufPool.Close() | defer outputBufPool.Close() | ||||
| readLens := math2.SplitLessThan(o.ChunkSize, 64*1024) | |||||
| readLenIdx := 0 | |||||
| for { | for { | ||||
| curReadLen := readLens[readLenIdx] | |||||
| for i := range inputChunks { | |||||
| inputChunks[i] = inputChunks[i][:curReadLen] | |||||
| } | |||||
| err := sync2.ParallelDo(inputs, func(s *exec.StreamValue, i int) error { | err := sync2.ParallelDo(inputs, func(s *exec.StreamValue, i int) error { | ||||
| _, err := io.ReadFull(s.Stream, inputChunks[i]) | _, err := io.ReadFull(s.Stream, inputChunks[i]) | ||||
| return err | return err | ||||
| }) | }) | ||||
| if err == io.EOF { | if err == io.EOF { | ||||
| fut.SetVoid() | |||||
| return | return | ||||
| } | } | ||||
| if err != nil { | if err != nil { | ||||
| @@ -96,9 +87,6 @@ func (o *ECMultiply) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| if !ok { | if !ok { | ||||
| return | return | ||||
| } | } | ||||
| for i := range outputBuf { | |||||
| outputBuf[i] = outputBuf[i][:curReadLen] | |||||
| } | |||||
| err = mul.Multiply(o.Coef, inputChunks, outputBuf) | err = mul.Multiply(o.Coef, inputChunks, outputBuf) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -107,7 +95,6 @@ func (o *ECMultiply) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| } | } | ||||
| outputBufPool.PutFilled(outputBuf) | outputBufPool.PutFilled(outputBuf) | ||||
| readLenIdx = (readLenIdx + 1) % len(readLens) | |||||
| } | } | ||||
| }() | }() | ||||
| @@ -117,6 +104,7 @@ func (o *ECMultiply) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| for { | for { | ||||
| outputChunks, ok := outputBufPool.GetFilled() | outputChunks, ok := outputBufPool.GetFilled() | ||||
| if !ok { | if !ok { | ||||
| fut.SetVoid() | |||||
| return | return | ||||
| } | } | ||||
| @@ -35,3 +35,7 @@ type FileInfoValue struct { | |||||
| func (v *FileInfoValue) Clone() exec.VarValue { | func (v *FileInfoValue) Clone() exec.VarValue { | ||||
| return &FileInfoValue{v.FileInfo} | return &FileInfoValue{v.FileInfo} | ||||
| } | } | ||||
| func init() { | |||||
| exec.UseVarValue[*FileInfoValue]() | |||||
| } | |||||
| @@ -11,6 +11,7 @@ import ( | |||||
| func init() { | func init() { | ||||
| exec.UseOp[*S2STransfer]() | exec.UseOp[*S2STransfer]() | ||||
| exec.UseOp[*S2STransferDyn]() | |||||
| } | } | ||||
| type S2STransfer struct { | type S2STransfer struct { | ||||
| @@ -45,7 +46,12 @@ func (o *S2STransfer) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| } | } | ||||
| func (o *S2STransfer) String() string { | func (o *S2STransfer) String() string { | ||||
| return fmt.Sprintf("S2STransfer %v:%v -> %v:%v", o.SrcSpace.UserSpace.Storage.String(), o.SrcPath, o.DstSpace.UserSpace.Storage.String(), o.Output) | |||||
| return fmt.Sprintf("S2STransfer %v:%v -> %v:%v, %v", | |||||
| o.SrcSpace.UserSpace.Storage.String(), | |||||
| o.SrcPath, | |||||
| o.DstSpace.UserSpace.Storage.String(), | |||||
| o.DstPath, | |||||
| o.Output) | |||||
| } | } | ||||
| type S2STransferDyn struct { | type S2STransferDyn struct { | ||||
| @@ -85,7 +91,12 @@ func (o *S2STransferDyn) Execute(ctx *exec.ExecContext, e *exec.Executor) error | |||||
| } | } | ||||
| func (o *S2STransferDyn) String() string { | func (o *S2STransferDyn) String() string { | ||||
| return fmt.Sprintf("S2STransferDyn %v:%v -> %v:%v", o.SrcSpace.UserSpace.Storage.String(), o.SrcFileInfo, o.DstSpace.UserSpace.Storage.String(), o.Output) | |||||
| return fmt.Sprintf("S2STransferDyn %v:%v -> %v:%v, %v", | |||||
| o.SrcSpace.UserSpace.Storage.String(), | |||||
| o.SrcFileInfo, | |||||
| o.DstSpace.UserSpace.Storage.String(), | |||||
| o.DstPath, | |||||
| o.Output) | |||||
| } | } | ||||
| type S2STransferNode struct { | type S2STransferNode struct { | ||||
| @@ -42,7 +42,7 @@ func (o *GetShardInfo) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| } | } | ||||
| func (o *GetShardInfo) String() string { | func (o *GetShardInfo) String() string { | ||||
| return fmt.Sprintf("GetShardInfo(%v)", o.FileHash) | |||||
| return fmt.Sprintf("GetShardInfo: %v:%v-> %v", o.UserSpace, o.FileHash, o.ShardInfo) | |||||
| } | } | ||||
| type StoreShard struct { | type StoreShard struct { | ||||
| @@ -79,7 +79,7 @@ func (o *StoreShard) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| } | } | ||||
| func (o *StoreShard) String() string { | func (o *StoreShard) String() string { | ||||
| return fmt.Sprintf("StoreShard: addInfo=%v, shardInfo=%v", o.FileInfo, o.ShardInfo) | |||||
| return fmt.Sprintf("StoreShard(space=%v): %v -> %v", o.UserSpace, o.FileInfo, o.ShardInfo) | |||||
| } | } | ||||
| type GetShardInfoNode struct { | type GetShardInfoNode struct { | ||||
| @@ -113,7 +113,6 @@ func Extend(ctx *state.GenerateState) error { | |||||
| if err != nil { | if err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| ctx.FromNodes[fr] = frNode | |||||
| ctx.IndexedStreams = append(ctx.IndexedStreams, state.IndexedStream{ | ctx.IndexedStreams = append(ctx.IndexedStreams, state.IndexedStream{ | ||||
| Stream: frNode.Output().Var(), | Stream: frNode.Output().Var(), | ||||
| @@ -229,7 +228,6 @@ func Extend(ctx *state.GenerateState) error { | |||||
| if err != nil { | if err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| ctx.ToNodes[to] = toNode | |||||
| str := findOutputStream(ctx, to.GetStreamIndex()) | str := findOutputStream(ctx, to.GetStreamIndex()) | ||||
| if str == nil { | if str == nil { | ||||
| @@ -266,6 +264,8 @@ func buildFromNode(ctx *state.GenerateState, f ioswitch2.From) (ops2.FromNode, e | |||||
| read := ctx.DAG.NewBaseReadDyn(f, f.UserSpace, types.DefaultOpen()) | read := ctx.DAG.NewBaseReadDyn(f, f.UserSpace, types.DefaultOpen()) | ||||
| getShard.FileInfoVar().ToSlot(read.FileInfoSlot()) | |||||
| if f.StreamIndex.IsRaw() { | if f.StreamIndex.IsRaw() { | ||||
| read.Option.WithNullableLength(repRange.Offset, repRange.Length) | read.Option.WithNullableLength(repRange.Offset, repRange.Length) | ||||
| } else if f.StreamIndex.IsEC() { | } else if f.StreamIndex.IsEC() { | ||||
| @@ -393,10 +393,10 @@ func setEnvBySpace(n dag.Node, space *clitypes.UserSpaceDetail) error { | |||||
| switch addr := space.RecommendHub.Address.(type) { | switch addr := space.RecommendHub.Address.(type) { | ||||
| case *cortypes.HttpAddressInfo: | case *cortypes.HttpAddressInfo: | ||||
| n.Env().ToEnvWorker(&ioswitch2.HttpHubWorker{Hub: *space.RecommendHub}, false) | |||||
| n.Env().ToEnvWorker(&ioswitch2.HttpHubWorker{Hub: *space.RecommendHub}, true) | |||||
| case *cortypes.GRPCAddressInfo: | case *cortypes.GRPCAddressInfo: | ||||
| n.Env().ToEnvWorker(&ioswitch2.HubWorker{Hub: *space.RecommendHub, Address: *addr}, false) | |||||
| n.Env().ToEnvWorker(&ioswitch2.HubWorker{Hub: *space.RecommendHub, Address: *addr}, true) | |||||
| default: | default: | ||||
| return fmt.Errorf("unsupported node address type %T", addr) | return fmt.Errorf("unsupported node address type %T", addr) | ||||
| @@ -123,7 +123,6 @@ func UseECMultiplier(ctx *state.GenerateState) { | |||||
| // 只有完全没有输出的ShardReadNode才可以被删除 | // 只有完全没有输出的ShardReadNode才可以被删除 | ||||
| if brNode.Output().Var().Dst.Len() == 0 { | if brNode.Output().Var().Dst.Len() == 0 { | ||||
| ctx.DAG.RemoveNode(brNode) | ctx.DAG.RemoveNode(brNode) | ||||
| delete(ctx.FromNodes, brNode.From) | |||||
| } | } | ||||
| hbr := ctx.DAG.NewGetShardHTTPRequest(brNode.UserSpace, fromShards[i].FileHash) | hbr := ctx.DAG.NewGetShardHTTPRequest(brNode.UserSpace, fromShards[i].FileHash) | ||||
| @@ -133,7 +132,6 @@ func UseECMultiplier(ctx *state.GenerateState) { | |||||
| for i, bwNode := range bwNodes { | for i, bwNode := range bwNodes { | ||||
| ctx.DAG.RemoveNode(bwNode) | ctx.DAG.RemoveNode(bwNode) | ||||
| delete(ctx.ToNodes, bwNode.To) | |||||
| for _, dstSlot := range bwNode.FileInfoVar().ListDstSlots() { | for _, dstSlot := range bwNode.FileInfoVar().ListDstSlots() { | ||||
| callMul.FileInfoVar(i).ToSlot(dstSlot) | callMul.FileInfoVar(i).ToSlot(dstSlot) | ||||
| @@ -7,16 +7,38 @@ import ( | |||||
| "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2/parser/state" | "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2/parser/state" | ||||
| ) | ) | ||||
| // 删除未使用的From流,不会删除FromDriver | |||||
| func RemoveUnusedFromNode(ctx *state.GenerateState) { | |||||
| dag.WalkOnlyType[ops2.FromNode](ctx.DAG.Graph, func(node ops2.FromNode) bool { | |||||
| if _, ok := node.(*ops2.FromDriverNode); ok { | |||||
| return true | |||||
| // 删除未使用的BaseRead指令,如果是FromNode则会一并从FromNodes中删除。 | |||||
| // 对于BaseReadDyn指令,如果它的上级是GetShardInfo,且只有一个输出,则会一并删除这个节点 | |||||
| func RemoveUnusedBaseRead(ctx *state.GenerateState) { | |||||
| dag.WalkOnlyType[*ops2.BaseReadNode](ctx.DAG.Graph, func(node *ops2.BaseReadNode) bool { | |||||
| if node.Output().Var().Dst.Len() == 0 { | |||||
| ctx.DAG.RemoveNode(node) | |||||
| } | } | ||||
| return true | |||||
| }) | |||||
| dag.WalkOnlyType[*ops2.BaseReadDynNode](ctx.DAG.Graph, func(node *ops2.BaseReadDynNode) bool { | |||||
| if node.Output().Var().Dst.Len() == 0 { | if node.Output().Var().Dst.Len() == 0 { | ||||
| ctx.DAG.RemoveNode(node) | ctx.DAG.RemoveNode(node) | ||||
| srcVar := node.FileInfoSlot().Var() | |||||
| if srcVar == nil { | |||||
| return true | |||||
| } | |||||
| srcVar.NotTo(node) | |||||
| // 暂时限定只处理GetShardInfo | |||||
| _, ok := srcVar.Src.(*ops2.GetShardInfoNode) | |||||
| if !ok { | |||||
| return true | |||||
| } | |||||
| if srcVar.Dst.Len() == 0 { | |||||
| ctx.DAG.RemoveNode(srcVar.Src) | |||||
| } | |||||
| } | } | ||||
| return true | return true | ||||
| }) | }) | ||||
| } | } | ||||
| @@ -52,7 +74,9 @@ func StoreShardWriteResult(ctx *state.GenerateState) { | |||||
| // 生成Range指令。StreamRange可能超过文件总大小,但Range指令会在数据量不够时不报错而是正常返回 | // 生成Range指令。StreamRange可能超过文件总大小,但Range指令会在数据量不够时不报错而是正常返回 | ||||
| func GenerateRange(ctx *state.GenerateState) { | func GenerateRange(ctx *state.GenerateState) { | ||||
| for to, toNode := range ctx.ToNodes { | |||||
| dag.WalkOnlyType[ops2.ToNode](ctx.DAG.Graph, func(toNode ops2.ToNode) bool { | |||||
| to := toNode.GetTo() | |||||
| toStrIdx := to.GetStreamIndex() | toStrIdx := to.GetStreamIndex() | ||||
| toRng := to.GetRange() | toRng := to.GetRange() | ||||
| @@ -103,7 +127,9 @@ func GenerateRange(ctx *state.GenerateState) { | |||||
| // toInput.Var().NotTo(toNode, toInput.Index) | // toInput.Var().NotTo(toNode, toInput.Index) | ||||
| // toNode.SetInput(rnged) | // toNode.SetInput(rnged) | ||||
| } | } | ||||
| } | |||||
| return true | |||||
| }) | |||||
| } | } | ||||
| // 生成Clone指令 | // 生成Clone指令 | ||||
| @@ -97,7 +97,6 @@ func UseMultipartUploadToShardStore(ctx *state.GenerateState) { | |||||
| // 最后删除Join指令和ToShardStore指令 | // 最后删除Join指令和ToShardStore指令 | ||||
| ctx.DAG.RemoveNode(joinNode) | ctx.DAG.RemoveNode(joinNode) | ||||
| ctx.DAG.RemoveNode(bwNode) | ctx.DAG.RemoveNode(bwNode) | ||||
| delete(ctx.ToNodes, bwNode.GetTo()) | |||||
| return true | return true | ||||
| }) | }) | ||||
| } | } | ||||
| @@ -1,7 +1,7 @@ | |||||
| package opt | package opt | ||||
| import ( | import ( | ||||
| "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/dag" | |||||
| "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2/ops2" | "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/ioswitch2/parser/state" | ||||
| "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/factory" | "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/factory" | ||||
| @@ -14,133 +14,62 @@ func UseS2STransfer(ctx *state.GenerateState) { | |||||
| return | return | ||||
| } | } | ||||
| for fr, frNode := range ctx.FromNodes { | |||||
| switch fr := fr.(type) { | |||||
| case *ioswitch2.FromShardStore: | |||||
| s2sFromShardStore(ctx, fr, frNode) | |||||
| case *ioswitch2.FromBaseStore: | |||||
| s2sFromBaseStore(ctx, fr, frNode) | |||||
| dag.WalkOnlyType[*ops2.BaseWriteNode](ctx.DAG.Graph, func(node *ops2.BaseWriteNode) bool { | |||||
| inputVar := node.Input().Var() | |||||
| if inputVar == nil { | |||||
| return true | |||||
| } | } | ||||
| } | |||||
| } | |||||
| func s2sFromShardStore(ctx *state.GenerateState, fromShard *ioswitch2.FromShardStore, frNode ops2.FromNode) { | |||||
| fromStgBld := factory.GetBuilder(&fromShard.UserSpace) | |||||
| s2s, err := fromStgBld.CreateS2STransfer(true) | |||||
| if err != nil { | |||||
| return | |||||
| } | |||||
| // 此输出流的所有目的地都要能支持S2S传输 | |||||
| outVar := frNode.Output().Var() | |||||
| if outVar.Dst.Len() == 0 { | |||||
| return | |||||
| } | |||||
| failed := false | |||||
| var toBases []*ops2.BaseWriteNode | |||||
| loop: | |||||
| for i := 0; i < outVar.Dst.Len(); i++ { | |||||
| dstNode := outVar.Dst.Get(i) | |||||
| switch dstNode := dstNode.(type) { | |||||
| case *ops2.BaseWriteNode: | |||||
| if !s2s.CanTransfer(&fromShard.UserSpace, &dstNode.UserSpace) { | |||||
| failed = true | |||||
| break | |||||
| } | |||||
| toBases = append(toBases, dstNode) | |||||
| default: | |||||
| failed = true | |||||
| break loop | |||||
| } | |||||
| } | |||||
| if failed { | |||||
| return | |||||
| } | |||||
| for _, toBase := range toBases { | |||||
| s2sNode := ctx.DAG.NewS2STransferDyn(fromShard.UserSpace, toBase.UserSpace, toBase.Path) | |||||
| // 直传指令在目的地Hub上执行 | |||||
| s2sNode.Env().CopyFrom(toBase.Env()) | |||||
| // 先获取文件路径,送到S2S节点 | |||||
| gsNode := ctx.DAG.NewGetShardInfo(fromShard.UserSpace, fromShard.FileHash) | |||||
| gsNode.Env().ToEnvDriver(true) | |||||
| gsNode.FileInfoVar().ToSlot(s2sNode.SrcFileInfoSlot()) | |||||
| // 原本BaseWriteNode的FileInfoVar被替换成S2SNode的FileInfoVar | |||||
| for _, dstSlot := range toBase.FileInfoVar().ListDstSlots() { | |||||
| s2sNode.FileInfoVar().ToSlot(dstSlot) | |||||
| s2s, err := factory.GetBuilder(&node.UserSpace).CreateS2STransfer(true) | |||||
| if err != nil { | |||||
| return true | |||||
| } | } | ||||
| // 从计划中删除目标节点 | |||||
| ctx.DAG.RemoveNode(toBase) | |||||
| delete(ctx.ToNodes, toBase.To) | |||||
| } | |||||
| // 从计划中删除源节点 | |||||
| ctx.DAG.RemoveNode(frNode) | |||||
| delete(ctx.FromNodes, frNode.GetFrom()) | |||||
| } | |||||
| // 只有BaseRead->BaseWrite的情况才可以进行S2S传输 | |||||
| switch inputNode := inputVar.Src.(type) { | |||||
| case *ops2.BaseReadNode: | |||||
| if !s2s.CanTransfer(&inputNode.UserSpace, &node.UserSpace) { | |||||
| return true | |||||
| } | |||||
| func s2sFromBaseStore(ctx *state.GenerateState, fromBase *ioswitch2.FromBaseStore, frNode ops2.FromNode) { | |||||
| fromStgBld := factory.GetBuilder(&fromBase.UserSpace) | |||||
| s2s, err := fromStgBld.CreateS2STransfer(true) | |||||
| if err != nil { | |||||
| return | |||||
| } | |||||
| s2sNode := ctx.DAG.NewS2STransfer(inputNode.UserSpace, inputNode.Path, node.UserSpace, node.Path) | |||||
| // 直传指令在目的地Hub上执行 | |||||
| s2sNode.Env().CopyFrom(node.Env()) | |||||
| // 此输出流的所有目的地都要能支持S2S传输 | |||||
| outVar := frNode.Output().Var() | |||||
| if outVar.Dst.Len() == 0 { | |||||
| return | |||||
| } | |||||
| // 原本BaseWriteNode的FileInfoVar被替换成S2SNode的FileInfoVar | |||||
| for _, dstSlot := range node.FileInfoVar().ListDstSlots() { | |||||
| s2sNode.FileInfoVar().ToSlot(dstSlot) | |||||
| } | |||||
| failed := false | |||||
| var toBases []*ops2.BaseWriteNode | |||||
| case *ops2.BaseReadDynNode: | |||||
| if !s2s.CanTransfer(&inputNode.UserSpace, &node.UserSpace) { | |||||
| return true | |||||
| } | |||||
| loop: | |||||
| for i := 0; i < outVar.Dst.Len(); i++ { | |||||
| dstNode := outVar.Dst.Get(i) | |||||
| s2sNode := ctx.DAG.NewS2STransferDyn(inputNode.UserSpace, node.UserSpace, node.Path) | |||||
| // 直传指令在目的地Hub上执行 | |||||
| s2sNode.Env().CopyFrom(node.Env()) | |||||
| switch dstNode := dstNode.(type) { | |||||
| case *ops2.BaseWriteNode: | |||||
| if !s2s.CanTransfer(&fromBase.UserSpace, &dstNode.UserSpace) { | |||||
| failed = true | |||||
| break | |||||
| // 原本BaseWriteNode的FileInfoVar被替换成S2SNode的FileInfoVar | |||||
| for _, dstSlot := range node.FileInfoVar().ListDstSlots() { | |||||
| s2sNode.FileInfoVar().ToSlot(dstSlot) | |||||
| } | } | ||||
| toBases = append(toBases, dstNode) | |||||
| // 传递给BaseReadDyn的FileInfo也给S2S一份 | |||||
| srcFileInfoVar := inputNode.FileInfoSlot().Var() | |||||
| if srcFileInfoVar != nil { | |||||
| srcFileInfoVar.ToSlot(s2sNode.SrcFileInfoSlot()) | |||||
| } | |||||
| default: | default: | ||||
| failed = true | |||||
| break loop | |||||
| return true | |||||
| } | } | ||||
| } | |||||
| if failed { | |||||
| return | |||||
| } | |||||
| for _, toBase := range toBases { | |||||
| s2sNode := ctx.DAG.NewS2STransfer(fromBase.UserSpace, fromBase.Path, toBase.UserSpace, toBase.Path) | |||||
| // 直传指令在目的地Hub上执行 | |||||
| s2sNode.Env().CopyFrom(toBase.Env()) | |||||
| // 原本BaseWriteNode的FileInfoVar被替换成S2SNode的FileInfoVar | |||||
| for _, dstSlot := range toBase.FileInfoVar().ListDstSlots() { | |||||
| s2sNode.FileInfoVar().ToSlot(dstSlot) | |||||
| } | |||||
| // 中断srcVar的流向 | |||||
| inputVar.NotTo(node) | |||||
| // 从计划中删除目标节点 | // 从计划中删除目标节点 | ||||
| ctx.DAG.RemoveNode(toBase) | |||||
| delete(ctx.ToNodes, toBase.To) | |||||
| } | |||||
| // 从计划中删除源节点 | |||||
| ctx.DAG.RemoveNode(frNode) | |||||
| delete(ctx.FromNodes, frNode.GetFrom()) | |||||
| ctx.DAG.RemoveNode(node) | |||||
| return true | |||||
| }) | |||||
| } | } | ||||
| @@ -17,10 +17,10 @@ func setEnvBySpace(n dag.Node, space *clitypes.UserSpaceDetail) error { | |||||
| switch addr := space.RecommendHub.Address.(type) { | switch addr := space.RecommendHub.Address.(type) { | ||||
| case *cortypes.HttpAddressInfo: | case *cortypes.HttpAddressInfo: | ||||
| n.Env().ToEnvWorker(&ioswitch2.HttpHubWorker{Hub: *space.RecommendHub}, false) | |||||
| n.Env().ToEnvWorker(&ioswitch2.HttpHubWorker{Hub: *space.RecommendHub}, true) | |||||
| case *cortypes.GRPCAddressInfo: | case *cortypes.GRPCAddressInfo: | ||||
| n.Env().ToEnvWorker(&ioswitch2.HubWorker{Hub: *space.RecommendHub, Address: *addr}, false) | |||||
| n.Env().ToEnvWorker(&ioswitch2.HubWorker{Hub: *space.RecommendHub, Address: *addr}, true) | |||||
| default: | default: | ||||
| return fmt.Errorf("unsupported node address type %T", addr) | return fmt.Errorf("unsupported node address type %T", addr) | ||||
| @@ -76,10 +76,10 @@ func Parse(ft ioswitch2.FromTo, blder *exec.PlanBuilder) error { | |||||
| } | } | ||||
| // 下面这些只需要执行一次,但需要按顺序 | // 下面这些只需要执行一次,但需要按顺序 | ||||
| opt.RemoveUnusedFromNode(state) | |||||
| opt.UseECMultiplier(state) | |||||
| opt.UseS2STransfer(state) | opt.UseS2STransfer(state) | ||||
| opt.UseECMultiplier(state) | |||||
| opt.UseMultipartUploadToShardStore(state) | opt.UseMultipartUploadToShardStore(state) | ||||
| opt.RemoveUnusedBaseRead(state) | |||||
| opt.DropUnused(state) | opt.DropUnused(state) | ||||
| opt.StoreShardWriteResult(state) | opt.StoreShardWriteResult(state) | ||||
| opt.GenerateRange(state) | opt.GenerateRange(state) | ||||
| @@ -17,19 +17,15 @@ type GenerateState struct { | |||||
| DAG *ops2.GraphNodeBuilder | DAG *ops2.GraphNodeBuilder | ||||
| // 为了产生所有To所需的数据范围,而需要From打开的范围。 | // 为了产生所有To所需的数据范围,而需要From打开的范围。 | ||||
| // 这个范围是基于整个文件的,且上下界都取整到条带大小的整数倍,因此上界是有可能超过文件大小的。 | // 这个范围是基于整个文件的,且上下界都取整到条带大小的整数倍,因此上界是有可能超过文件大小的。 | ||||
| ToNodes map[ioswitch2.To]ops2.ToNode | |||||
| FromNodes map[ioswitch2.From]ops2.FromNode | |||||
| IndexedStreams []IndexedStream | |||||
| StreamRange math2.Range | StreamRange math2.Range | ||||
| IndexedStreams []IndexedStream | |||||
| UseEC bool // 是否使用纠删码 | UseEC bool // 是否使用纠删码 | ||||
| UseSegment bool // 是否使用分段 | UseSegment bool // 是否使用分段 | ||||
| } | } | ||||
| func InitGenerateState(ft ioswitch2.FromTo) *GenerateState { | func InitGenerateState(ft ioswitch2.FromTo) *GenerateState { | ||||
| return &GenerateState{ | return &GenerateState{ | ||||
| Ft: ft, | |||||
| DAG: ops2.NewGraphNodeBuilder(), | |||||
| ToNodes: make(map[ioswitch2.To]ops2.ToNode), | |||||
| FromNodes: make(map[ioswitch2.From]ops2.FromNode), | |||||
| Ft: ft, | |||||
| DAG: ops2.NewGraphNodeBuilder(), | |||||
| } | } | ||||
| } | } | ||||
| @@ -17,10 +17,10 @@ func setEnvBySpace(n dag.Node, space *clitypes.UserSpaceDetail) error { | |||||
| switch addr := space.RecommendHub.Address.(type) { | switch addr := space.RecommendHub.Address.(type) { | ||||
| case *cortypes.HttpAddressInfo: | case *cortypes.HttpAddressInfo: | ||||
| n.Env().ToEnvWorker(&ioswitch2.HttpHubWorker{Hub: *space.RecommendHub}, false) | |||||
| n.Env().ToEnvWorker(&ioswitch2.HttpHubWorker{Hub: *space.RecommendHub}, true) | |||||
| case *cortypes.GRPCAddressInfo: | case *cortypes.GRPCAddressInfo: | ||||
| n.Env().ToEnvWorker(&ioswitch2.HubWorker{Hub: *space.RecommendHub, Address: *addr}, false) | |||||
| n.Env().ToEnvWorker(&ioswitch2.HubWorker{Hub: *space.RecommendHub, Address: *addr}, true) | |||||
| default: | default: | ||||
| return fmt.Errorf("unsupported node address type %T", addr) | return fmt.Errorf("unsupported node address type %T", addr) | ||||
| @@ -18,6 +18,7 @@ import ( | |||||
| func init() { | func init() { | ||||
| exec.UseOp[*BaseWrite]() | exec.UseOp[*BaseWrite]() | ||||
| exec.UseOp[*BaseRead]() | exec.UseOp[*BaseRead]() | ||||
| exec.UseOp[*BaseReadDyn]() | |||||
| } | } | ||||
| type BaseRead struct { | type BaseRead struct { | ||||
| @@ -65,14 +66,14 @@ func (o *BaseRead) String() string { | |||||
| return fmt.Sprintf("PublicRead %v:%v -> %v", o.UserSpace, o.Path, o.Output) | return fmt.Sprintf("PublicRead %v:%v -> %v", o.UserSpace, o.Path, o.Output) | ||||
| } | } | ||||
| type BaseReadPathVar struct { | |||||
| type BaseReadDyn struct { | |||||
| UserSpace clitypes.UserSpaceDetail | UserSpace clitypes.UserSpaceDetail | ||||
| Output exec.VarID | Output exec.VarID | ||||
| Path exec.VarID | Path exec.VarID | ||||
| Option types.OpenOption | Option types.OpenOption | ||||
| } | } | ||||
| func (o *BaseReadPathVar) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| func (o *BaseReadDyn) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| logger. | logger. | ||||
| WithField("Output", o.Output). | WithField("Output", o.Output). | ||||
| WithField("UserSpace", o.UserSpace). | WithField("UserSpace", o.UserSpace). | ||||
| @@ -111,7 +112,7 @@ func (o *BaseReadPathVar) Execute(ctx *exec.ExecContext, e *exec.Executor) error | |||||
| return fut.Wait(ctx.Context) | return fut.Wait(ctx.Context) | ||||
| } | } | ||||
| func (o *BaseReadPathVar) String() string { | |||||
| func (o *BaseReadDyn) String() string { | |||||
| return fmt.Sprintf("BaseReadPathVar %v:%v -> %v", o.UserSpace, o.Path, o.Output) | return fmt.Sprintf("BaseReadPathVar %v:%v -> %v", o.UserSpace, o.Path, o.Output) | ||||
| } | } | ||||
| @@ -159,56 +160,6 @@ func (o *BaseWrite) String() string { | |||||
| return fmt.Sprintf("PublicWrite %v -> %v:%v", o.Input, o.UserSpace, o.Path) | return fmt.Sprintf("PublicWrite %v -> %v:%v", o.Input, o.UserSpace, o.Path) | ||||
| } | } | ||||
| // type BaseWriteTemp struct { | |||||
| // Input exec.VarID | |||||
| // UserSpace clitypes.UserSpaceDetail | |||||
| // Path string | |||||
| // Signal exec.VarID | |||||
| // WriteResult exec.VarID | |||||
| // } | |||||
| // func (o *BaseWriteTemp) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| // logger. | |||||
| // WithField("Input", o.Input). | |||||
| // Debugf("write file to base store") | |||||
| // defer logger.Debugf("write file to base store finished") | |||||
| // stgPool, err := exec.GetValueByType[*pool.Pool](ctx) | |||||
| // if err != nil { | |||||
| // return fmt.Errorf("getting storage pool: %w", err) | |||||
| // } | |||||
| // _, err = exec.BindVar[*exec.SignalValue](e, ctx.Context, o.Signal) | |||||
| // if err != nil { | |||||
| // return err | |||||
| // } | |||||
| // store, err := stgPool.GetBaseStore(&o.UserSpace) | |||||
| // if err != nil { | |||||
| // return fmt.Errorf("getting base store of storage %v: %w", o.UserSpace, err) | |||||
| // } | |||||
| // input, err := exec.BindVar[*exec.StreamValue](e, ctx.Context, o.Input) | |||||
| // if err != nil { | |||||
| // return err | |||||
| // } | |||||
| // defer input.Stream.Close() | |||||
| // ret, err := store.Write(o.Path, input.Stream) | |||||
| // if err != nil { | |||||
| // return err | |||||
| // } | |||||
| // e.PutVar(o.WriteResult, &WriteResultValue{ | |||||
| // WriteResult: ret, | |||||
| // }) | |||||
| // return nil | |||||
| // } | |||||
| // func (o *BaseWriteTemp) String() string { | |||||
| // return fmt.Sprintf("PublicWriteTemp[signal: %v] %v -> %v:%v", o.Signal, o.Input, o.UserSpace, o.Path) | |||||
| // } | |||||
| type BaseReadNode struct { | type BaseReadNode struct { | ||||
| dag.NodeBase | dag.NodeBase | ||||
| From ioswitchlrc.From | From ioswitchlrc.From | ||||
| @@ -250,15 +201,15 @@ func (t *BaseReadNode) GenerateOp() (exec.Op, error) { | |||||
| }, nil | }, nil | ||||
| } | } | ||||
| type BaseReadPathVarNode struct { | |||||
| type BaseReadDynNode struct { | |||||
| dag.NodeBase | dag.NodeBase | ||||
| From ioswitchlrc.From | From ioswitchlrc.From | ||||
| UserSpace clitypes.UserSpaceDetail | UserSpace clitypes.UserSpaceDetail | ||||
| Option types.OpenOption | Option types.OpenOption | ||||
| } | } | ||||
| func (b *GraphNodeBuilder) NewBaseReadPathVar(from ioswitchlrc.From, userSpace clitypes.UserSpaceDetail, opt types.OpenOption) *BaseReadPathVarNode { | |||||
| node := &BaseReadPathVarNode{ | |||||
| func (b *GraphNodeBuilder) NewBaseReadDyn(from ioswitchlrc.From, userSpace clitypes.UserSpaceDetail, opt types.OpenOption) *BaseReadDynNode { | |||||
| node := &BaseReadDynNode{ | |||||
| From: from, | From: from, | ||||
| UserSpace: userSpace, | UserSpace: userSpace, | ||||
| Option: opt, | Option: opt, | ||||
| @@ -270,26 +221,26 @@ func (b *GraphNodeBuilder) NewBaseReadPathVar(from ioswitchlrc.From, userSpace c | |||||
| return node | return node | ||||
| } | } | ||||
| func (t *BaseReadPathVarNode) GetFrom() ioswitchlrc.From { | |||||
| func (t *BaseReadDynNode) GetFrom() ioswitchlrc.From { | |||||
| return t.From | return t.From | ||||
| } | } | ||||
| func (t *BaseReadPathVarNode) Output() dag.StreamOutputSlot { | |||||
| func (t *BaseReadDynNode) Output() dag.StreamOutputSlot { | |||||
| return dag.StreamOutputSlot{ | return dag.StreamOutputSlot{ | ||||
| Node: t, | Node: t, | ||||
| Index: 0, | Index: 0, | ||||
| } | } | ||||
| } | } | ||||
| func (t *BaseReadPathVarNode) PathVar() dag.ValueInputSlot { | |||||
| func (t *BaseReadDynNode) PathVar() dag.ValueInputSlot { | |||||
| return dag.ValueInputSlot{ | return dag.ValueInputSlot{ | ||||
| Node: t, | Node: t, | ||||
| Index: 0, | Index: 0, | ||||
| } | } | ||||
| } | } | ||||
| func (t *BaseReadPathVarNode) GenerateOp() (exec.Op, error) { | |||||
| return &BaseReadPathVar{ | |||||
| func (t *BaseReadDynNode) GenerateOp() (exec.Op, error) { | |||||
| return &BaseReadDyn{ | |||||
| UserSpace: t.UserSpace, | UserSpace: t.UserSpace, | ||||
| Output: t.Output().Var().VarID, | Output: t.Output().Var().VarID, | ||||
| Path: t.PathVar().Var().VarID, | Path: t.PathVar().Var().VarID, | ||||
| @@ -343,58 +294,3 @@ func (t *BaseWriteNode) GenerateOp() (exec.Op, error) { | |||||
| WriteResult: t.FileInfoVar().Var().VarID, | WriteResult: t.FileInfoVar().Var().VarID, | ||||
| }, nil | }, nil | ||||
| } | } | ||||
| // type BaseWriteTempNode struct { | |||||
| // dag.NodeBase | |||||
| // To ioswitchlrc.To | |||||
| // UserSpace clitypes.UserSpaceDetail | |||||
| // Path string | |||||
| // } | |||||
| // func (b *GraphNodeBuilder) NewBaseWriteTemp(to ioswitchlrc.To, userSpace clitypes.UserSpaceDetail, path string) *BaseWriteTempNode { | |||||
| // node := &BaseWriteTempNode{ | |||||
| // To: to, | |||||
| // UserSpace: userSpace, | |||||
| // Path: path, | |||||
| // } | |||||
| // b.AddNode(node) | |||||
| // node.InputStreams().Init(1) | |||||
| // node.InputValues().Init(1) | |||||
| // return node | |||||
| // } | |||||
| // func (t *BaseWriteTempNode) GetTo() ioswitchlrc.To { | |||||
| // return t.To | |||||
| // } | |||||
| // func (t *BaseWriteTempNode) Input() dag.StreamInputSlot { | |||||
| // return dag.StreamInputSlot{ | |||||
| // Node: t, | |||||
| // Index: 0, | |||||
| // } | |||||
| // } | |||||
| // func (t *BaseWriteTempNode) TargetSignal() dag.ValueInputSlot { | |||||
| // return dag.ValueInputSlot{ | |||||
| // Node: t, | |||||
| // Index: 0, | |||||
| // } | |||||
| // } | |||||
| // func (t *BaseWriteTempNode) FileInfoVar() dag.ValueOutputSlot { | |||||
| // return dag.ValueOutputSlot{ | |||||
| // Node: t, | |||||
| // Index: 0, | |||||
| // } | |||||
| // } | |||||
| // func (t *BaseWriteTempNode) GenerateOp() (exec.Op, error) { | |||||
| // return &BaseWriteTemp{ | |||||
| // Input: t.Input().Var().VarID, | |||||
| // UserSpace: t.UserSpace, | |||||
| // Path: t.Path, | |||||
| // Signal: t.TargetSignal().Var().VarID, | |||||
| // WriteResult: t.FileInfoVar().Var().VarID, | |||||
| // }, nil | |||||
| // } | |||||
| @@ -66,7 +66,7 @@ func buildFromNode(ctx *GenerateContext, f ioswitchlrc.From) (ops2.FromNode, err | |||||
| getShard := ctx.DAG.NewGetShardInfo(f.UserSpace, f.FileHash) | getShard := ctx.DAG.NewGetShardInfo(f.UserSpace, f.FileHash) | ||||
| getShard.Env().ToEnvDriver(true) | getShard.Env().ToEnvDriver(true) | ||||
| read := ctx.DAG.NewBaseReadPathVar(f, f.UserSpace, types.DefaultOpen()) | |||||
| read := ctx.DAG.NewBaseReadDyn(f, f.UserSpace, types.DefaultOpen()) | |||||
| if f.DataIndex == -1 { | if f.DataIndex == -1 { | ||||
| read.Option.WithNullableLength(repRange.Offset, repRange.Length) | read.Option.WithNullableLength(repRange.Offset, repRange.Length) | ||||
| @@ -17,10 +17,10 @@ func setEnvBySpace(n dag.Node, space *clitypes.UserSpaceDetail) error { | |||||
| switch addr := space.RecommendHub.Address.(type) { | switch addr := space.RecommendHub.Address.(type) { | ||||
| case *cortypes.HttpAddressInfo: | case *cortypes.HttpAddressInfo: | ||||
| n.Env().ToEnvWorker(&ioswitch2.HttpHubWorker{Hub: *space.RecommendHub}, false) | |||||
| n.Env().ToEnvWorker(&ioswitch2.HttpHubWorker{Hub: *space.RecommendHub}, true) | |||||
| case *cortypes.GRPCAddressInfo: | case *cortypes.GRPCAddressInfo: | ||||
| n.Env().ToEnvWorker(&ioswitch2.HubWorker{Hub: *space.RecommendHub, Address: *addr}, false) | |||||
| n.Env().ToEnvWorker(&ioswitch2.HubWorker{Hub: *space.RecommendHub, Address: *addr}, true) | |||||
| default: | default: | ||||
| return fmt.Errorf("unsupported node address type %T", addr) | return fmt.Errorf("unsupported node address type %T", addr) | ||||
| @@ -235,6 +235,11 @@ func DownloadStreamServer[Resp DownloadStreamResp, Req any, APIRet DownloadStrea | |||||
| return makeCodeError(errorcode.OperationFailed, err.Error()) | return makeCodeError(errorcode.OperationFailed, err.Error()) | ||||
| } | } | ||||
| err = cw.Finish() | |||||
| if err != nil { | |||||
| return makeCodeError(errorcode.OperationFailed, err.Error()) | |||||
| } | |||||
| return nil | return nil | ||||
| } | } | ||||
| @@ -18,6 +18,10 @@ type S2STransfer struct { | |||||
| // 只有同一个机器的存储之间才可以进行数据直传 | // 只有同一个机器的存储之间才可以进行数据直传 | ||||
| func (*S2STransfer) CanTransfer(src, dst *clitypes.UserSpaceDetail) bool { | func (*S2STransfer) CanTransfer(src, dst *clitypes.UserSpaceDetail) bool { | ||||
| if types.FindFeature[*cortypes.S2STransferFeature](dst) == nil { | |||||
| return false | |||||
| } | |||||
| _, ok := src.UserSpace.Storage.(*cortypes.LocalType) | _, ok := src.UserSpace.Storage.(*cortypes.LocalType) | ||||
| if !ok { | if !ok { | ||||
| return false | return false | ||||
| @@ -14,27 +14,29 @@ import ( | |||||
| ) | ) | ||||
| type ShardStore struct { | type ShardStore struct { | ||||
| detail *clitypes.UserSpaceDetail | |||||
| absRoot string | |||||
| lock sync.Mutex | |||||
| done chan any | |||||
| detail *clitypes.UserSpaceDetail | |||||
| stgRoot string | |||||
| storeAbsRoot string | |||||
| lock sync.Mutex | |||||
| done chan any | |||||
| } | } | ||||
| func NewShardStore(root string, detail *clitypes.UserSpaceDetail) (*ShardStore, error) { | func NewShardStore(root string, detail *clitypes.UserSpaceDetail) (*ShardStore, error) { | ||||
| absRoot, err := filepath.Abs(filepath.Join(root, detail.UserSpace.WorkingDir, types.ShardStoreWorkingDir)) | |||||
| storeAbsRoot, err := filepath.Abs(filepath.Join(root, detail.UserSpace.WorkingDir, types.ShardStoreWorkingDir)) | |||||
| if err != nil { | if err != nil { | ||||
| return nil, fmt.Errorf("get abs root: %w", err) | return nil, fmt.Errorf("get abs root: %w", err) | ||||
| } | } | ||||
| return &ShardStore{ | return &ShardStore{ | ||||
| detail: detail, | |||||
| absRoot: absRoot, | |||||
| done: make(chan any, 1), | |||||
| detail: detail, | |||||
| stgRoot: root, | |||||
| storeAbsRoot: storeAbsRoot, | |||||
| done: make(chan any, 1), | |||||
| }, nil | }, nil | ||||
| } | } | ||||
| func (s *ShardStore) Start(ch *types.StorageEventChan) { | func (s *ShardStore) Start(ch *types.StorageEventChan) { | ||||
| s.getLogger().Infof("component start, root: %v, max size: %v", s.absRoot, s.detail.UserSpace.ShardStore.MaxSize) | |||||
| s.getLogger().Infof("component start, root: %v, max size: %v", s.storeAbsRoot, s.detail.UserSpace.ShardStore.MaxSize) | |||||
| } | } | ||||
| func (s *ShardStore) Stop() { | func (s *ShardStore) Stop() { | ||||
| @@ -42,12 +44,14 @@ func (s *ShardStore) Stop() { | |||||
| } | } | ||||
| func (s *ShardStore) Store(path string, hash clitypes.FileHash, size int64) (types.FileInfo, error) { | func (s *ShardStore) Store(path string, hash clitypes.FileHash, size int64) (types.FileInfo, error) { | ||||
| fullTempPath := filepath.Join(s.stgRoot, path) | |||||
| s.lock.Lock() | s.lock.Lock() | ||||
| defer s.lock.Unlock() | defer s.lock.Unlock() | ||||
| log := s.getLogger() | log := s.getLogger() | ||||
| log.Debugf("%v bypass uploaded, size: %v, hash: %v", path, size, hash) | |||||
| log.Debugf("%v bypass uploaded, size: %v, hash: %v", fullTempPath, size, hash) | |||||
| blockDir := s.getFileDirFromHash(hash) | blockDir := s.getFileDirFromHash(hash) | ||||
| err := os.MkdirAll(blockDir, 0755) | err := os.MkdirAll(blockDir, 0755) | ||||
| @@ -59,9 +63,9 @@ func (s *ShardStore) Store(path string, hash clitypes.FileHash, size int64) (typ | |||||
| newPath := filepath.Join(blockDir, string(hash)) | newPath := filepath.Join(blockDir, string(hash)) | ||||
| _, err = os.Stat(newPath) | _, err = os.Stat(newPath) | ||||
| if os.IsNotExist(err) { | if os.IsNotExist(err) { | ||||
| err = os.Rename(path, newPath) | |||||
| err = os.Rename(fullTempPath, newPath) | |||||
| if err != nil { | if err != nil { | ||||
| log.Warnf("rename %v to %v: %v", path, newPath, err) | |||||
| log.Warnf("rename %v to %v: %v", fullTempPath, newPath, err) | |||||
| return types.FileInfo{}, fmt.Errorf("rename file: %w", err) | return types.FileInfo{}, fmt.Errorf("rename file: %w", err) | ||||
| } | } | ||||
| @@ -90,7 +94,7 @@ func (s *ShardStore) Info(hash clitypes.FileHash) (types.FileInfo, error) { | |||||
| return types.FileInfo{ | return types.FileInfo{ | ||||
| Hash: hash, | Hash: hash, | ||||
| Size: info.Size(), | Size: info.Size(), | ||||
| Path: filePath, | |||||
| Path: s.getSlashFilePathFromHash(hash), | |||||
| }, nil | }, nil | ||||
| } | } | ||||
| @@ -100,7 +104,7 @@ func (s *ShardStore) ListAll() ([]types.FileInfo, error) { | |||||
| var infos []types.FileInfo | var infos []types.FileInfo | ||||
| err := filepath.WalkDir(s.absRoot, func(path string, d fs.DirEntry, err error) error { | |||||
| err := filepath.WalkDir(s.storeAbsRoot, func(path string, d fs.DirEntry, err error) error { | |||||
| if err != nil { | if err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| @@ -144,7 +148,7 @@ func (s *ShardStore) GC(avaiables []clitypes.FileHash) error { | |||||
| cnt := 0 | cnt := 0 | ||||
| err := filepath.WalkDir(s.absRoot, func(path string, d fs.DirEntry, err error) error { | |||||
| err := filepath.WalkDir(s.storeAbsRoot, func(path string, d fs.DirEntry, err error) error { | |||||
| if err != nil { | if err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| @@ -196,11 +200,11 @@ func (s *ShardStore) getLogger() logger.Logger { | |||||
| } | } | ||||
| func (s *ShardStore) getFileDirFromHash(hash clitypes.FileHash) string { | func (s *ShardStore) getFileDirFromHash(hash clitypes.FileHash) string { | ||||
| return filepath.Join(s.absRoot, hash.GetHashPrefix(2)) | |||||
| return filepath.Join(s.storeAbsRoot, hash.GetHashPrefix(2)) | |||||
| } | } | ||||
| func (s *ShardStore) getFilePathFromHash(hash clitypes.FileHash) string { | func (s *ShardStore) getFilePathFromHash(hash clitypes.FileHash) string { | ||||
| return filepath.Join(s.absRoot, hash.GetHashPrefix(2), string(hash)) | |||||
| return filepath.Join(s.storeAbsRoot, hash.GetHashPrefix(2), string(hash)) | |||||
| } | } | ||||
| func (s *ShardStore) getSlashFilePathFromHash(hash clitypes.FileHash) string { | func (s *ShardStore) getSlashFilePathFromHash(hash clitypes.FileHash) string { | ||||
| @@ -78,7 +78,7 @@ func (User) TableName() string { | |||||
| } | } | ||||
| type HubLocation struct { | type HubLocation struct { | ||||
| HubID HubID `gorm:"column:HubID; primaryKey; type:bigint" json:"hubID"` | |||||
| HubID HubID `gorm:"column:HubID; type:bigint" json:"hubID"` | |||||
| StorageName string `gorm:"column:StorageName; type:varchar(255); not null" json:"storageName"` | StorageName string `gorm:"column:StorageName; type:varchar(255); not null" json:"storageName"` | ||||
| Location string `gorm:"column:Location; type:varchar(255); not null" json:"location"` | Location string `gorm:"column:Location; type:varchar(255); not null" json:"location"` | ||||
| } | } | ||||