| @@ -19,8 +19,10 @@ import ( | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/connectivity" | "gitlink.org.cn/cloudream/storage/common/pkgs/connectivity" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/distlock" | "gitlink.org.cn/cloudream/storage/common/pkgs/distlock" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/downloader" | "gitlink.org.cn/cloudream/storage/common/pkgs/downloader" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/downloader/strategy" | |||||
| agtrpc "gitlink.org.cn/cloudream/storage/common/pkgs/grpc/agent" | agtrpc "gitlink.org.cn/cloudream/storage/common/pkgs/grpc/agent" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/svcmgr" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/metacache" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/agtpool" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/uploader" | "gitlink.org.cn/cloudream/storage/common/pkgs/uploader" | ||||
| "google.golang.org/grpc" | "google.golang.org/grpc" | ||||
| @@ -46,16 +48,16 @@ func serve(configPath string) { | |||||
| } | } | ||||
| stgglb.InitLocal(&config.Cfg().Local) | stgglb.InitLocal(&config.Cfg().Local) | ||||
| stgglb.InitMQPool(&config.Cfg().RabbitMQ) | |||||
| stgglb.InitMQPool(config.Cfg().RabbitMQ) | |||||
| stgglb.InitAgentRPCPool(&agtrpc.PoolConfig{}) | stgglb.InitAgentRPCPool(&agtrpc.PoolConfig{}) | ||||
| // 获取Hub配置 | // 获取Hub配置 | ||||
| hubCfg := downloadHubConfig() | hubCfg := downloadHubConfig() | ||||
| // 初始化存储服务管理器 | // 初始化存储服务管理器 | ||||
| stgMgr := svcmgr.NewManager() | |||||
| stgAgts := agtpool.NewPool() | |||||
| for _, stg := range hubCfg.Storages { | for _, stg := range hubCfg.Storages { | ||||
| err := stgMgr.CreateService(stg) | |||||
| err := stgAgts.SetupAgent(stg) | |||||
| if err != nil { | if err != nil { | ||||
| fmt.Printf("init storage %v: %v", stg.Storage.String(), err) | fmt.Printf("init storage %v: %v", stg.Storage.String(), err) | ||||
| os.Exit(1) | os.Exit(1) | ||||
| @@ -66,7 +68,7 @@ func serve(configPath string) { | |||||
| worker := exec.NewWorker() | worker := exec.NewWorker() | ||||
| // 初始化HTTP服务 | // 初始化HTTP服务 | ||||
| httpSvr, err := http.NewServer(config.Cfg().ListenAddr, http.NewService(&worker, stgMgr)) | |||||
| httpSvr, err := http.NewServer(config.Cfg().ListenAddr, http.NewService(&worker, stgAgts)) | |||||
| if err != nil { | if err != nil { | ||||
| logger.Fatalf("new http server failed, err: %s", err.Error()) | logger.Fatalf("new http server failed, err: %s", err.Error()) | ||||
| } | } | ||||
| @@ -87,15 +89,15 @@ func serve(configPath string) { | |||||
| hubCons := make([]cdssdk.HubConnectivity, 0, len(cons)) | hubCons := make([]cdssdk.HubConnectivity, 0, len(cons)) | ||||
| for _, con := range cons { | for _, con := range cons { | ||||
| var delay *float32 | var delay *float32 | ||||
| if con.Delay != nil { | |||||
| v := float32(con.Delay.Microseconds()) / 1000 | |||||
| if con.Latency != nil { | |||||
| v := float32(con.Latency.Microseconds()) / 1000 | |||||
| delay = &v | delay = &v | ||||
| } | } | ||||
| hubCons = append(hubCons, cdssdk.HubConnectivity{ | hubCons = append(hubCons, cdssdk.HubConnectivity{ | ||||
| FromHubID: *stgglb.Local.HubID, | FromHubID: *stgglb.Local.HubID, | ||||
| ToHubID: con.ToHubID, | ToHubID: con.ToHubID, | ||||
| Delay: delay, | |||||
| Latency: delay, | |||||
| TestTime: con.TestTime, | TestTime: con.TestTime, | ||||
| }) | }) | ||||
| } | } | ||||
| @@ -107,6 +109,13 @@ func serve(configPath string) { | |||||
| }) | }) | ||||
| conCol.CollectInPlace() | conCol.CollectInPlace() | ||||
| // 初始化元数据缓存服务 | |||||
| metacacheHost := metacache.NewHost() | |||||
| go metacacheHost.Serve() | |||||
| stgMeta := metacacheHost.AddStorageMeta() | |||||
| hubMeta := metacacheHost.AddHubMeta() | |||||
| conMeta := metacacheHost.AddConnectivity() | |||||
| // 启动访问统计服务 | // 启动访问统计服务 | ||||
| acStat := accessstat.NewAccessStat(accessstat.Config{ | acStat := accessstat.NewAccessStat(accessstat.Config{ | ||||
| // TODO 考虑放到配置里 | // TODO 考虑放到配置里 | ||||
| @@ -120,18 +129,21 @@ func serve(configPath string) { | |||||
| logger.Fatalf("new ipfs failed, err: %s", err.Error()) | logger.Fatalf("new ipfs failed, err: %s", err.Error()) | ||||
| } | } | ||||
| // 初始化下载策略选择器 | |||||
| strgSel := strategy.NewSelector(config.Cfg().DownloadStrategy, stgMeta, hubMeta, conMeta) | |||||
| // 初始化下载器 | // 初始化下载器 | ||||
| dlder := downloader.NewDownloader(config.Cfg().Downloader, &conCol, stgMgr) | |||||
| dlder := downloader.NewDownloader(config.Cfg().Downloader, &conCol, stgAgts, strgSel) | |||||
| // 初始化上传器 | // 初始化上传器 | ||||
| uploader := uploader.NewUploader(distlock, &conCol, stgMgr) | |||||
| uploader := uploader.NewUploader(distlock, &conCol, stgAgts, stgMeta) | |||||
| // 初始化任务管理器 | // 初始化任务管理器 | ||||
| taskMgr := task.NewManager(distlock, &conCol, &dlder, acStat, stgMgr, uploader) | |||||
| taskMgr := task.NewManager(distlock, &conCol, &dlder, acStat, stgAgts, uploader) | |||||
| // 启动命令服务器 | // 启动命令服务器 | ||||
| // TODO 需要设计AgentID持久化机制 | // TODO 需要设计AgentID持久化机制 | ||||
| agtSvr, err := agtmq.NewServer(cmdsvc.NewService(&taskMgr, stgMgr), config.Cfg().ID, &config.Cfg().RabbitMQ) | |||||
| agtSvr, err := agtmq.NewServer(cmdsvc.NewService(&taskMgr, stgAgts), config.Cfg().ID, config.Cfg().RabbitMQ) | |||||
| if err != nil { | if err != nil { | ||||
| logger.Fatalf("new agent server failed, err: %s", err.Error()) | logger.Fatalf("new agent server failed, err: %s", err.Error()) | ||||
| } | } | ||||
| @@ -147,7 +159,7 @@ func serve(configPath string) { | |||||
| logger.Fatalf("listen on %s failed, err: %s", listenAddr, err.Error()) | logger.Fatalf("listen on %s failed, err: %s", listenAddr, err.Error()) | ||||
| } | } | ||||
| s := grpc.NewServer() | s := grpc.NewServer() | ||||
| agtrpc.RegisterAgentServer(s, grpcsvc.NewService(&worker, stgMgr)) | |||||
| agtrpc.RegisterAgentServer(s, grpcsvc.NewService(&worker, stgAgts)) | |||||
| go serveGRPC(s, lis) | go serveGRPC(s, lis) | ||||
| go serveDistLock(distlock) | go serveDistLock(distlock) | ||||
| @@ -3,25 +3,27 @@ package config | |||||
| import ( | import ( | ||||
| "gitlink.org.cn/cloudream/common/pkgs/distlock" | "gitlink.org.cn/cloudream/common/pkgs/distlock" | ||||
| log "gitlink.org.cn/cloudream/common/pkgs/logger" | log "gitlink.org.cn/cloudream/common/pkgs/logger" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/mq" | |||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | ||||
| c "gitlink.org.cn/cloudream/common/utils/config" | c "gitlink.org.cn/cloudream/common/utils/config" | ||||
| stgmodels "gitlink.org.cn/cloudream/storage/common/models" | stgmodels "gitlink.org.cn/cloudream/storage/common/models" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/connectivity" | "gitlink.org.cn/cloudream/storage/common/pkgs/connectivity" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/downloader" | "gitlink.org.cn/cloudream/storage/common/pkgs/downloader" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/downloader/strategy" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/grpc" | "gitlink.org.cn/cloudream/storage/common/pkgs/grpc" | ||||
| stgmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq" | |||||
| ) | ) | ||||
| type Config struct { | type Config struct { | ||||
| ID cdssdk.HubID `json:"id"` | |||||
| ListenAddr string `json:"listenAddr"` | |||||
| Local stgmodels.LocalMachineInfo `json:"local"` | |||||
| GRPC *grpc.Config `json:"grpc"` | |||||
| Logger log.Config `json:"logger"` | |||||
| RabbitMQ stgmq.Config `json:"rabbitMQ"` | |||||
| DistLock distlock.Config `json:"distlock"` | |||||
| Connectivity connectivity.Config `json:"connectivity"` | |||||
| Downloader downloader.Config `json:"downloader"` | |||||
| ID cdssdk.HubID `json:"id"` | |||||
| ListenAddr string `json:"listenAddr"` | |||||
| Local stgmodels.LocalMachineInfo `json:"local"` | |||||
| GRPC *grpc.Config `json:"grpc"` | |||||
| Logger log.Config `json:"logger"` | |||||
| RabbitMQ mq.Config `json:"rabbitMQ"` | |||||
| DistLock distlock.Config `json:"distlock"` | |||||
| Connectivity connectivity.Config `json:"connectivity"` | |||||
| Downloader downloader.Config `json:"downloader"` | |||||
| DownloadStrategy strategy.Config `json:"downloadStrategy"` | |||||
| } | } | ||||
| var cfg Config | var cfg Config | ||||
| @@ -29,7 +29,7 @@ func (s *Service) ExecuteIOPlan(ctx context.Context, req *agtrpc.ExecuteIOPlanRe | |||||
| defer s.swWorker.Remove(sw) | defer s.swWorker.Remove(sw) | ||||
| execCtx := exec.NewWithContext(ctx) | execCtx := exec.NewWithContext(ctx) | ||||
| exec.SetValueByType(execCtx, s.stgMgr) | |||||
| exec.SetValueByType(execCtx, s.stgAgts) | |||||
| _, err = sw.Run(execCtx) | _, err = sw.Run(execCtx) | ||||
| if err != nil { | if err != nil { | ||||
| log.Warnf("running io plan: %v", err) | log.Warnf("running io plan: %v", err) | ||||
| @@ -3,18 +3,18 @@ package grpc | |||||
| import ( | import ( | ||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | ||||
| agentserver "gitlink.org.cn/cloudream/storage/common/pkgs/grpc/agent" | agentserver "gitlink.org.cn/cloudream/storage/common/pkgs/grpc/agent" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/svcmgr" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/agtpool" | |||||
| ) | ) | ||||
| type Service struct { | type Service struct { | ||||
| agentserver.AgentServer | agentserver.AgentServer | ||||
| swWorker *exec.Worker | swWorker *exec.Worker | ||||
| stgMgr *svcmgr.Manager | |||||
| stgAgts *agtpool.AgentPool | |||||
| } | } | ||||
| func NewService(swWorker *exec.Worker, stgMgr *svcmgr.Manager) *Service { | |||||
| func NewService(swWorker *exec.Worker, stgAgts *agtpool.AgentPool) *Service { | |||||
| return &Service{ | return &Service{ | ||||
| swWorker: swWorker, | swWorker: swWorker, | ||||
| stgMgr: stgMgr, | |||||
| stgAgts: stgAgts, | |||||
| } | } | ||||
| } | } | ||||
| @@ -162,7 +162,7 @@ func (s *IOService) ExecuteIOPlan(ctx *gin.Context) { | |||||
| defer s.svc.swWorker.Remove(sw) | defer s.svc.swWorker.Remove(sw) | ||||
| execCtx := exec.NewWithContext(ctx.Request.Context()) | execCtx := exec.NewWithContext(ctx.Request.Context()) | ||||
| exec.SetValueByType(execCtx, s.svc.stgMgr) | |||||
| exec.SetValueByType(execCtx, s.svc.stgAgts) | |||||
| _, err = sw.Run(execCtx) | _, err = sw.Run(execCtx) | ||||
| if err != nil { | if err != nil { | ||||
| ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("executing plan: %v", err))) | ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("executing plan: %v", err))) | ||||
| @@ -2,17 +2,17 @@ package http | |||||
| import ( | import ( | ||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/svcmgr" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/agtpool" | |||||
| ) | ) | ||||
| type Service struct { | type Service struct { | ||||
| swWorker *exec.Worker | swWorker *exec.Worker | ||||
| stgMgr *svcmgr.Manager | |||||
| stgAgts *agtpool.AgentPool | |||||
| } | } | ||||
| func NewService(swWorker *exec.Worker, stgMgr *svcmgr.Manager) *Service { | |||||
| func NewService(swWorker *exec.Worker, stgAgts *agtpool.AgentPool) *Service { | |||||
| return &Service{ | return &Service{ | ||||
| swWorker: swWorker, | swWorker: swWorker, | ||||
| stgMgr: stgMgr, | |||||
| stgAgts: stgAgts, | |||||
| } | } | ||||
| } | } | ||||
| @@ -12,7 +12,7 @@ import ( | |||||
| ) | ) | ||||
| func (svc *Service) CheckCache(msg *agtmq.CheckCache) (*agtmq.CheckCacheResp, *mq.CodeMessage) { | func (svc *Service) CheckCache(msg *agtmq.CheckCache) (*agtmq.CheckCacheResp, *mq.CodeMessage) { | ||||
| store, err := svc.stgMgr.GetShardStore(msg.StorageID) | |||||
| store, err := svc.stgAgts.GetShardStore(msg.StorageID) | |||||
| if err != nil { | if err != nil { | ||||
| return nil, mq.Failed(errorcode.OperationFailed, fmt.Sprintf("get shard store of storage %v: %v", msg.StorageID, err)) | return nil, mq.Failed(errorcode.OperationFailed, fmt.Sprintf("get shard store of storage %v: %v", msg.StorageID, err)) | ||||
| } | } | ||||
| @@ -31,7 +31,7 @@ func (svc *Service) CheckCache(msg *agtmq.CheckCache) (*agtmq.CheckCacheResp, *m | |||||
| } | } | ||||
| func (svc *Service) CacheGC(msg *agtmq.CacheGC) (*agtmq.CacheGCResp, *mq.CodeMessage) { | func (svc *Service) CacheGC(msg *agtmq.CacheGC) (*agtmq.CacheGCResp, *mq.CodeMessage) { | ||||
| store, err := svc.stgMgr.GetShardStore(msg.StorageID) | |||||
| store, err := svc.stgAgts.GetShardStore(msg.StorageID) | |||||
| if err != nil { | if err != nil { | ||||
| return nil, mq.Failed(errorcode.OperationFailed, fmt.Sprintf("get shard store of storage %v: %v", msg.StorageID, err)) | return nil, mq.Failed(errorcode.OperationFailed, fmt.Sprintf("get shard store of storage %v: %v", msg.StorageID, err)) | ||||
| } | } | ||||
| @@ -2,17 +2,17 @@ package mq | |||||
| import ( | import ( | ||||
| "gitlink.org.cn/cloudream/storage/agent/internal/task" | "gitlink.org.cn/cloudream/storage/agent/internal/task" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/svcmgr" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/agtpool" | |||||
| ) | ) | ||||
| type Service struct { | type Service struct { | ||||
| taskManager *task.Manager | taskManager *task.Manager | ||||
| stgMgr *svcmgr.Manager | |||||
| stgAgts *agtpool.AgentPool | |||||
| } | } | ||||
| func NewService(taskMgr *task.Manager, stgMgr *svcmgr.Manager) *Service { | |||||
| func NewService(taskMgr *task.Manager, stgAgts *agtpool.AgentPool) *Service { | |||||
| return &Service{ | return &Service{ | ||||
| taskManager: taskMgr, | taskManager: taskMgr, | ||||
| stgMgr: stgMgr, | |||||
| stgAgts: stgAgts, | |||||
| } | } | ||||
| } | } | ||||
| @@ -4,104 +4,11 @@ import ( | |||||
| "time" | "time" | ||||
| "gitlink.org.cn/cloudream/common/consts/errorcode" | "gitlink.org.cn/cloudream/common/consts/errorcode" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/logger" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/mq" | "gitlink.org.cn/cloudream/common/pkgs/mq" | ||||
| mytask "gitlink.org.cn/cloudream/storage/agent/internal/task" | mytask "gitlink.org.cn/cloudream/storage/agent/internal/task" | ||||
| stgglb "gitlink.org.cn/cloudream/storage/common/globals" | |||||
| stgmod "gitlink.org.cn/cloudream/storage/common/models" | |||||
| agtmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/agent" | agtmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/agent" | ||||
| ) | ) | ||||
| func (svc *Service) StartStorageLoadPackage(msg *agtmq.StartStorageLoadPackage) (*agtmq.StartStorageLoadPackageResp, *mq.CodeMessage) { | |||||
| tsk := svc.taskManager.StartNew(mytask.NewStorageLoadPackage(msg.UserID, msg.PackageID, msg.StorageID)) | |||||
| return mq.ReplyOK(agtmq.NewStartStorageLoadPackageResp(tsk.ID())) | |||||
| } | |||||
| func (svc *Service) WaitStorageLoadPackage(msg *agtmq.WaitStorageLoadPackage) (*agtmq.WaitStorageLoadPackageResp, *mq.CodeMessage) { | |||||
| logger.WithField("TaskID", msg.TaskID).Debugf("wait loading package") | |||||
| tsk := svc.taskManager.FindByID(msg.TaskID) | |||||
| if tsk == nil { | |||||
| return nil, mq.Failed(errorcode.TaskNotFound, "task not found") | |||||
| } | |||||
| if msg.WaitTimeoutMs == 0 { | |||||
| tsk.Wait() | |||||
| errMsg := "" | |||||
| if tsk.Error() != nil { | |||||
| errMsg = tsk.Error().Error() | |||||
| } | |||||
| loadTsk := tsk.Body().(*mytask.StorageLoadPackage) | |||||
| return mq.ReplyOK(agtmq.NewWaitStorageLoadPackageResp(true, errMsg, loadTsk.PackagePath, loadTsk.LocalBase, loadTsk.RemoteBase)) | |||||
| } else { | |||||
| if tsk.WaitTimeout(time.Duration(msg.WaitTimeoutMs) * time.Millisecond) { | |||||
| errMsg := "" | |||||
| if tsk.Error() != nil { | |||||
| errMsg = tsk.Error().Error() | |||||
| } | |||||
| loadTsk := tsk.Body().(*mytask.StorageLoadPackage) | |||||
| return mq.ReplyOK(agtmq.NewWaitStorageLoadPackageResp(true, errMsg, loadTsk.PackagePath, loadTsk.LocalBase, loadTsk.RemoteBase)) | |||||
| } | |||||
| return mq.ReplyOK(agtmq.NewWaitStorageLoadPackageResp(false, "", "", "", "")) | |||||
| } | |||||
| } | |||||
| func (svc *Service) StorageCheck(msg *agtmq.StorageCheck) (*agtmq.StorageCheckResp, *mq.CodeMessage) { | |||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | |||||
| if err != nil { | |||||
| return nil, mq.Failed(errorcode.OperationFailed, err.Error()) | |||||
| } | |||||
| defer stgglb.CoordinatorMQPool.Release(coorCli) | |||||
| shared, err := svc.stgMgr.GetSharedStore(msg.StorageID) | |||||
| if err != nil { | |||||
| return nil, mq.Failed(errorcode.OperationFailed, err.Error()) | |||||
| } | |||||
| loaded, err := shared.ListLoadedPackages() | |||||
| if err != nil { | |||||
| return nil, mq.Failed(errorcode.OperationFailed, err.Error()) | |||||
| } | |||||
| return mq.ReplyOK(agtmq.NewStorageCheckResp(loaded)) | |||||
| } | |||||
| func (svc *Service) StorageGC(msg *agtmq.StorageGC) (*agtmq.StorageGCResp, *mq.CodeMessage) { | |||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | |||||
| if err != nil { | |||||
| return nil, mq.Failed(errorcode.OperationFailed, err.Error()) | |||||
| } | |||||
| defer stgglb.CoordinatorMQPool.Release(coorCli) | |||||
| shared, err := svc.stgMgr.GetSharedStore(msg.StorageID) | |||||
| if err != nil { | |||||
| return nil, mq.Failed(errorcode.OperationFailed, err.Error()) | |||||
| } | |||||
| var loadeds []stgmod.LoadedPackageID | |||||
| for _, pkg := range msg.Packages { | |||||
| loadeds = append(loadeds, stgmod.LoadedPackageID{ | |||||
| UserID: pkg.UserID, | |||||
| PackageID: pkg.PackageID, | |||||
| }) | |||||
| } | |||||
| err = shared.PackageGC(loadeds) | |||||
| if err != nil { | |||||
| return nil, mq.Failed(errorcode.OperationFailed, err.Error()) | |||||
| } | |||||
| return mq.ReplyOK(agtmq.RespStorageGC()) | |||||
| } | |||||
| func (svc *Service) StartStorageCreatePackage(msg *agtmq.StartStorageCreatePackage) (*agtmq.StartStorageCreatePackageResp, *mq.CodeMessage) { | func (svc *Service) StartStorageCreatePackage(msg *agtmq.StartStorageCreatePackage) (*agtmq.StartStorageCreatePackageResp, *mq.CodeMessage) { | ||||
| return nil, mq.Failed(errorcode.OperationFailed, "not implemented") | return nil, mq.Failed(errorcode.OperationFailed, "not implemented") | ||||
| // coorCli, err := stgglb.CoordinatorMQPool.Acquire() | // coorCli, err := stgglb.CoordinatorMQPool.Acquire() | ||||
| @@ -39,7 +39,7 @@ func (t *CacheMovePackage) do(ctx TaskContext) error { | |||||
| log.Debugf("begin with %v", logger.FormatStruct(t)) | log.Debugf("begin with %v", logger.FormatStruct(t)) | ||||
| defer log.Debugf("end") | defer log.Debugf("end") | ||||
| store, err := ctx.stgMgr.GetShardStore(t.storageID) | |||||
| store, err := ctx.stgAgts.GetShardStore(t.storageID) | |||||
| if err != nil { | if err != nil { | ||||
| return fmt.Errorf("get shard store of storage %v: %w", t.storageID, err) | return fmt.Errorf("get shard store of storage %v: %w", t.storageID, err) | ||||
| } | } | ||||
| @@ -85,7 +85,7 @@ func (t *CreatePackage) Execute(task *task.Task[TaskContext], ctx TaskContext, c | |||||
| return | return | ||||
| } | } | ||||
| up, err := ctx.uploader.BeginUpdate(t.userID, createResp.Package.PackageID, t.stgAffinity) | |||||
| up, err := ctx.uploader.BeginUpdate(t.userID, createResp.Package.PackageID, t.stgAffinity, nil, nil) | |||||
| if err != nil { | if err != nil { | ||||
| err = fmt.Errorf("begin update: %w", err) | err = fmt.Errorf("begin update: %w", err) | ||||
| log.Error(err.Error()) | log.Error(err.Error()) | ||||
| @@ -1,339 +0,0 @@ | |||||
| package task | |||||
| import ( | |||||
| "fmt" | |||||
| "io" | |||||
| "math" | |||||
| "time" | |||||
| "github.com/samber/lo" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/bitmap" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/logger" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/task" | |||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | |||||
| "gitlink.org.cn/cloudream/common/utils/io2" | |||||
| "gitlink.org.cn/cloudream/common/utils/reflect2" | |||||
| "gitlink.org.cn/cloudream/common/utils/sort2" | |||||
| "gitlink.org.cn/cloudream/storage/common/consts" | |||||
| stgglb "gitlink.org.cn/cloudream/storage/common/globals" | |||||
| stgmod "gitlink.org.cn/cloudream/storage/common/models" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/distlock/reqbuilder" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ec" | |||||
| coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/types" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/utils" | |||||
| ) | |||||
| type StorageLoadPackage struct { | |||||
| PackagePath string | |||||
| LocalBase string | |||||
| RemoteBase string | |||||
| userID cdssdk.UserID | |||||
| packageID cdssdk.PackageID | |||||
| storageID cdssdk.StorageID | |||||
| pinnedBlocks []stgmod.ObjectBlock | |||||
| } | |||||
| func NewStorageLoadPackage(userID cdssdk.UserID, packageID cdssdk.PackageID, storageID cdssdk.StorageID) *StorageLoadPackage { | |||||
| return &StorageLoadPackage{ | |||||
| userID: userID, | |||||
| packageID: packageID, | |||||
| storageID: storageID, | |||||
| } | |||||
| } | |||||
| func (t *StorageLoadPackage) Execute(task *task.Task[TaskContext], ctx TaskContext, complete CompleteFn) { | |||||
| startTime := time.Now() | |||||
| log := logger.WithType[StorageLoadPackage]("Task") | |||||
| log.WithField("TaskID", task.ID()). | |||||
| Infof("begin to load package %v to %v", t.packageID, t.storageID) | |||||
| err := t.do(task, ctx) | |||||
| if err == nil { | |||||
| log.WithField("TaskID", task.ID()). | |||||
| Infof("loading success, cost: %v", time.Since(startTime)) | |||||
| } else { | |||||
| log.WithField("TaskID", task.ID()). | |||||
| Warnf("loading package: %v, cost: %v", err, time.Since(startTime)) | |||||
| } | |||||
| complete(err, CompleteOption{ | |||||
| RemovingDelay: time.Minute, | |||||
| }) | |||||
| } | |||||
| func (t *StorageLoadPackage) do(task *task.Task[TaskContext], ctx TaskContext) error { | |||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | |||||
| if err != nil { | |||||
| return fmt.Errorf("new coordinator client: %w", err) | |||||
| } | |||||
| defer stgglb.CoordinatorMQPool.Release(coorCli) | |||||
| shared, err := ctx.stgMgr.GetSharedStore(t.storageID) | |||||
| if err != nil { | |||||
| return fmt.Errorf("get shared store of storage %v: %w", t.storageID, err) | |||||
| } | |||||
| t.PackagePath = utils.MakeLoadedPackagePath(t.userID, t.packageID) | |||||
| getObjectDetails, err := coorCli.GetPackageObjectDetails(coormq.ReqGetPackageObjectDetails(t.packageID)) | |||||
| if err != nil { | |||||
| return fmt.Errorf("getting package object details: %w", err) | |||||
| } | |||||
| shardstore, err := ctx.stgMgr.GetShardStore(t.storageID) | |||||
| if err != nil { | |||||
| return fmt.Errorf("get shard store of storage %v: %w", t.storageID, err) | |||||
| } | |||||
| mutex, err := reqbuilder.NewBuilder(). | |||||
| // 提前占位 | |||||
| Metadata().StoragePackage().CreateOne(t.userID, t.storageID, t.packageID). | |||||
| // 保护在storage目录中下载的文件 | |||||
| Storage().Buzy(t.storageID). | |||||
| // 保护下载文件时同时保存到IPFS的文件 | |||||
| Shard().Buzy(t.storageID). | |||||
| MutexLock(ctx.distlock) | |||||
| if err != nil { | |||||
| return fmt.Errorf("acquire locks failed, err: %w", err) | |||||
| } | |||||
| defer mutex.Unlock() | |||||
| for _, obj := range getObjectDetails.Objects { | |||||
| err := t.downloadOne(coorCli, shardstore, shared, obj) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| ctx.accessStat.AddAccessCounter(obj.Object.ObjectID, t.packageID, t.storageID, 1) | |||||
| } | |||||
| _, err = coorCli.StoragePackageLoaded(coormq.NewStoragePackageLoaded(t.userID, t.storageID, t.packageID, t.pinnedBlocks)) | |||||
| if err != nil { | |||||
| return fmt.Errorf("loading package to storage: %w", err) | |||||
| } | |||||
| // TODO 要防止下载的临时文件被删除 | |||||
| return err | |||||
| } | |||||
| func (t *StorageLoadPackage) downloadOne(coorCli *coormq.Client, shardStore types.ShardStore, shared types.SharedStore, obj stgmod.ObjectDetail) error { | |||||
| var file io.ReadCloser | |||||
| switch red := obj.Object.Redundancy.(type) { | |||||
| case *cdssdk.NoneRedundancy: | |||||
| reader, err := t.downloadNoneOrRepObject(shardStore, obj) | |||||
| if err != nil { | |||||
| return fmt.Errorf("downloading object: %w", err) | |||||
| } | |||||
| file = reader | |||||
| case *cdssdk.RepRedundancy: | |||||
| reader, err := t.downloadNoneOrRepObject(shardStore, obj) | |||||
| if err != nil { | |||||
| return fmt.Errorf("downloading rep object: %w", err) | |||||
| } | |||||
| file = reader | |||||
| case *cdssdk.ECRedundancy: | |||||
| reader, pinnedBlocks, err := t.downloadECObject(coorCli, shardStore, obj, red) | |||||
| if err != nil { | |||||
| return fmt.Errorf("downloading ec object: %w", err) | |||||
| } | |||||
| file = reader | |||||
| t.pinnedBlocks = append(t.pinnedBlocks, pinnedBlocks...) | |||||
| default: | |||||
| return fmt.Errorf("unknow redundancy type: %v", reflect2.TypeOfValue(obj.Object.Redundancy)) | |||||
| } | |||||
| defer file.Close() | |||||
| if _, err := shared.WritePackageObject(t.userID, t.packageID, obj.Object.Path, file); err != nil { | |||||
| return fmt.Errorf("writting object to file: %w", err) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func (t *StorageLoadPackage) downloadNoneOrRepObject(shardStore types.ShardStore, obj stgmod.ObjectDetail) (io.ReadCloser, error) { | |||||
| if len(obj.Blocks) == 0 && len(obj.PinnedAt) == 0 { | |||||
| return nil, fmt.Errorf("no storage has this object") | |||||
| } | |||||
| file, err := shardStore.Open(types.NewOpen(obj.Object.FileHash)) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| return file, nil | |||||
| } | |||||
| func (t *StorageLoadPackage) downloadECObject(coorCli *coormq.Client, shardStore types.ShardStore, obj stgmod.ObjectDetail, ecRed *cdssdk.ECRedundancy) (io.ReadCloser, []stgmod.ObjectBlock, error) { | |||||
| allStorages, err := t.sortDownloadStorages(coorCli, obj) | |||||
| if err != nil { | |||||
| return nil, nil, err | |||||
| } | |||||
| bsc, blocks := t.getMinReadingBlockSolution(allStorages, ecRed.K) | |||||
| osc, _ := t.getMinReadingObjectSolution(allStorages, ecRed.K) | |||||
| if bsc < osc { | |||||
| var fileStrs []io.ReadCloser | |||||
| rs, err := ec.NewStreamRs(ecRed.K, ecRed.N, ecRed.ChunkSize) | |||||
| if err != nil { | |||||
| return nil, nil, fmt.Errorf("new rs: %w", err) | |||||
| } | |||||
| for i := range blocks { | |||||
| str, err := shardStore.Open(types.NewOpen(blocks[i].Block.FileHash)) | |||||
| if err != nil { | |||||
| for i -= 1; i >= 0; i-- { | |||||
| fileStrs[i].Close() | |||||
| } | |||||
| return nil, nil, fmt.Errorf("donwloading file: %w", err) | |||||
| } | |||||
| fileStrs = append(fileStrs, str) | |||||
| } | |||||
| fileReaders, filesCloser := io2.ToReaders(fileStrs) | |||||
| var indexes []int | |||||
| for _, b := range blocks { | |||||
| indexes = append(indexes, b.Block.Index) | |||||
| } | |||||
| outputs, outputsCloser := io2.ToReaders(rs.ReconstructData(fileReaders, indexes)) | |||||
| return io2.AfterReadClosed(io2.Length(io2.ChunkedJoin(outputs, int(ecRed.ChunkSize)), obj.Object.Size), func(c io.ReadCloser) { | |||||
| filesCloser() | |||||
| outputsCloser() | |||||
| }), nil, nil | |||||
| } | |||||
| // bsc >= osc,如果osc是MaxFloat64,那么bsc也一定是,也就意味着没有足够块来恢复文件 | |||||
| if osc == math.MaxFloat64 { | |||||
| return nil, nil, fmt.Errorf("no enough blocks to reconstruct the file, want %d, get only %d", ecRed.K, len(blocks)) | |||||
| } | |||||
| // 如果是直接读取的文件,那么就不需要Pin文件块 | |||||
| str, err := shardStore.Open(types.NewOpen(obj.Object.FileHash)) | |||||
| return str, nil, err | |||||
| } | |||||
| type downloadStorageInfo struct { | |||||
| Storage stgmod.StorageDetail | |||||
| ObjectPinned bool | |||||
| Blocks []stgmod.ObjectBlock | |||||
| Distance float64 | |||||
| } | |||||
| func (t *StorageLoadPackage) sortDownloadStorages(coorCli *coormq.Client, obj stgmod.ObjectDetail) ([]*downloadStorageInfo, error) { | |||||
| var stgIDs []cdssdk.StorageID | |||||
| for _, id := range obj.PinnedAt { | |||||
| if !lo.Contains(stgIDs, id) { | |||||
| stgIDs = append(stgIDs, id) | |||||
| } | |||||
| } | |||||
| for _, b := range obj.Blocks { | |||||
| if !lo.Contains(stgIDs, b.StorageID) { | |||||
| stgIDs = append(stgIDs, b.StorageID) | |||||
| } | |||||
| } | |||||
| getStgs, err := coorCli.GetStorageDetails(coormq.ReqGetStorageDetails(stgIDs)) | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("getting storage details: %w", err) | |||||
| } | |||||
| allStgs := make(map[cdssdk.StorageID]stgmod.StorageDetail) | |||||
| for _, stg := range getStgs.Storages { | |||||
| allStgs[stg.Storage.StorageID] = *stg | |||||
| } | |||||
| downloadStorageMap := make(map[cdssdk.StorageID]*downloadStorageInfo) | |||||
| for _, id := range obj.PinnedAt { | |||||
| storage, ok := downloadStorageMap[id] | |||||
| if !ok { | |||||
| mod := allStgs[id] | |||||
| storage = &downloadStorageInfo{ | |||||
| Storage: mod, | |||||
| ObjectPinned: true, | |||||
| Distance: t.getStorageDistance(mod), | |||||
| } | |||||
| downloadStorageMap[id] = storage | |||||
| } | |||||
| storage.ObjectPinned = true | |||||
| } | |||||
| for _, b := range obj.Blocks { | |||||
| storage, ok := downloadStorageMap[b.StorageID] | |||||
| if !ok { | |||||
| mod := allStgs[b.StorageID] | |||||
| storage = &downloadStorageInfo{ | |||||
| Storage: mod, | |||||
| Distance: t.getStorageDistance(mod), | |||||
| } | |||||
| downloadStorageMap[b.StorageID] = storage | |||||
| } | |||||
| storage.Blocks = append(storage.Blocks, b) | |||||
| } | |||||
| return sort2.Sort(lo.Values(downloadStorageMap), func(left, right *downloadStorageInfo) int { | |||||
| return sort2.Cmp(left.Distance, right.Distance) | |||||
| }), nil | |||||
| } | |||||
| type downloadBlock struct { | |||||
| Storage stgmod.StorageDetail | |||||
| Block stgmod.ObjectBlock | |||||
| } | |||||
| func (t *StorageLoadPackage) getMinReadingBlockSolution(sortedStorages []*downloadStorageInfo, k int) (float64, []downloadBlock) { | |||||
| gotBlocksMap := bitmap.Bitmap64(0) | |||||
| var gotBlocks []downloadBlock | |||||
| dist := float64(0.0) | |||||
| for _, n := range sortedStorages { | |||||
| for _, b := range n.Blocks { | |||||
| if !gotBlocksMap.Get(b.Index) { | |||||
| gotBlocks = append(gotBlocks, downloadBlock{ | |||||
| Storage: n.Storage, | |||||
| Block: b, | |||||
| }) | |||||
| gotBlocksMap.Set(b.Index, true) | |||||
| dist += n.Distance | |||||
| } | |||||
| if len(gotBlocks) >= k { | |||||
| return dist, gotBlocks | |||||
| } | |||||
| } | |||||
| } | |||||
| return math.MaxFloat64, gotBlocks | |||||
| } | |||||
| func (t *StorageLoadPackage) getMinReadingObjectSolution(sortedStorages []*downloadStorageInfo, k int) (float64, *stgmod.StorageDetail) { | |||||
| dist := math.MaxFloat64 | |||||
| var downloadStg *stgmod.StorageDetail | |||||
| for _, n := range sortedStorages { | |||||
| if n.ObjectPinned && float64(k)*n.Distance < dist { | |||||
| dist = float64(k) * n.Distance | |||||
| stg := n.Storage | |||||
| downloadStg = &stg | |||||
| } | |||||
| } | |||||
| return dist, downloadStg | |||||
| } | |||||
| func (t *StorageLoadPackage) getStorageDistance(stg stgmod.StorageDetail) float64 { | |||||
| if stgglb.Local.HubID != nil { | |||||
| if stg.MasterHub.HubID == *stgglb.Local.HubID { | |||||
| return consts.StorageDistanceSameStorage | |||||
| } | |||||
| } | |||||
| if stg.MasterHub.LocationID == stgglb.Local.LocationID { | |||||
| return consts.StorageDistanceSameLocation | |||||
| } | |||||
| return consts.StorageDistanceOther | |||||
| } | |||||
| @@ -6,7 +6,7 @@ import ( | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/accessstat" | "gitlink.org.cn/cloudream/storage/common/pkgs/accessstat" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/connectivity" | "gitlink.org.cn/cloudream/storage/common/pkgs/connectivity" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/downloader" | "gitlink.org.cn/cloudream/storage/common/pkgs/downloader" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/svcmgr" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/agtpool" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/uploader" | "gitlink.org.cn/cloudream/storage/common/pkgs/uploader" | ||||
| ) | ) | ||||
| @@ -16,7 +16,7 @@ type TaskContext struct { | |||||
| connectivity *connectivity.Collector | connectivity *connectivity.Collector | ||||
| downloader *downloader.Downloader | downloader *downloader.Downloader | ||||
| accessStat *accessstat.AccessStat | accessStat *accessstat.AccessStat | ||||
| stgMgr *svcmgr.Manager | |||||
| stgAgts *agtpool.AgentPool | |||||
| uploader *uploader.Uploader | uploader *uploader.Uploader | ||||
| } | } | ||||
| @@ -35,13 +35,13 @@ type Task = task.Task[TaskContext] | |||||
| // CompleteOption 类型定义了任务完成时的选项,可用于定制化任务完成的处理方式 | // CompleteOption 类型定义了任务完成时的选项,可用于定制化任务完成的处理方式 | ||||
| type CompleteOption = task.CompleteOption | type CompleteOption = task.CompleteOption | ||||
| func NewManager(distlock *distlock.Service, connectivity *connectivity.Collector, downloader *downloader.Downloader, accessStat *accessstat.AccessStat, stgMgr *svcmgr.Manager, uploader *uploader.Uploader) Manager { | |||||
| func NewManager(distlock *distlock.Service, connectivity *connectivity.Collector, downloader *downloader.Downloader, accessStat *accessstat.AccessStat, stgAgts *agtpool.AgentPool, uploader *uploader.Uploader) Manager { | |||||
| return task.NewManager(TaskContext{ | return task.NewManager(TaskContext{ | ||||
| distlock: distlock, | distlock: distlock, | ||||
| connectivity: connectivity, | connectivity: connectivity, | ||||
| downloader: downloader, | downloader: downloader, | ||||
| accessStat: accessStat, | accessStat: accessStat, | ||||
| stgMgr: stgMgr, | |||||
| stgAgts: stgAgts, | |||||
| uploader: uploader, | uploader: uploader, | ||||
| }) | }) | ||||
| } | } | ||||
| @@ -52,7 +52,7 @@ func getpByPath(cmdCtx *CommandContext, path string, output string) { | |||||
| return | return | ||||
| } | } | ||||
| pkg, err := cmdCtx.Cmdline.Svc.PackageSvc().GetByName(userID, comps[0], comps[1]) | |||||
| pkg, err := cmdCtx.Cmdline.Svc.PackageSvc().GetByFullName(userID, comps[0], comps[1]) | |||||
| if err != nil { | if err != nil { | ||||
| fmt.Println(err) | fmt.Println(err) | ||||
| return | return | ||||
| @@ -15,7 +15,7 @@ func init() { | |||||
| cmd := cobra.Command{ | cmd := cobra.Command{ | ||||
| Use: "load", | Use: "load", | ||||
| Short: "Load data from CDS to a storage service", | Short: "Load data from CDS to a storage service", | ||||
| Args: cobra.ExactArgs(2), | |||||
| Args: cobra.ExactArgs(3), | |||||
| Run: func(cmd *cobra.Command, args []string) { | Run: func(cmd *cobra.Command, args []string) { | ||||
| cmdCtx := GetCmdCtx(cmd) | cmdCtx := GetCmdCtx(cmd) | ||||
| @@ -30,9 +30,9 @@ func init() { | |||||
| fmt.Printf("Invalid storage ID: %s\n", args[1]) | fmt.Printf("Invalid storage ID: %s\n", args[1]) | ||||
| } | } | ||||
| loadByID(cmdCtx, cdssdk.PackageID(pkgID), cdssdk.StorageID(stgID)) | |||||
| loadByID(cmdCtx, cdssdk.PackageID(pkgID), cdssdk.StorageID(stgID), args[2]) | |||||
| } else { | } else { | ||||
| loadByPath(cmdCtx, args[0], args[1]) | |||||
| loadByPath(cmdCtx, args[0], args[1], args[2]) | |||||
| } | } | ||||
| }, | }, | ||||
| } | } | ||||
| @@ -40,7 +40,7 @@ func init() { | |||||
| rootCmd.AddCommand(&cmd) | rootCmd.AddCommand(&cmd) | ||||
| } | } | ||||
| func loadByPath(cmdCtx *CommandContext, pkgPath string, stgName string) { | |||||
| func loadByPath(cmdCtx *CommandContext, pkgPath string, stgName string, rootPath string) { | |||||
| userID := cdssdk.UserID(1) | userID := cdssdk.UserID(1) | ||||
| comps := strings.Split(strings.Trim(pkgPath, cdssdk.ObjectPathSeparator), cdssdk.ObjectPathSeparator) | comps := strings.Split(strings.Trim(pkgPath, cdssdk.ObjectPathSeparator), cdssdk.ObjectPathSeparator) | ||||
| @@ -49,7 +49,7 @@ func loadByPath(cmdCtx *CommandContext, pkgPath string, stgName string) { | |||||
| return | return | ||||
| } | } | ||||
| pkg, err := cmdCtx.Cmdline.Svc.PackageSvc().GetByName(userID, comps[0], comps[1]) | |||||
| pkg, err := cmdCtx.Cmdline.Svc.PackageSvc().GetByFullName(userID, comps[0], comps[1]) | |||||
| if err != nil { | if err != nil { | ||||
| fmt.Println(err) | fmt.Println(err) | ||||
| return | return | ||||
| @@ -61,29 +61,18 @@ func loadByPath(cmdCtx *CommandContext, pkgPath string, stgName string) { | |||||
| return | return | ||||
| } | } | ||||
| loadByID(cmdCtx, pkg.PackageID, stg.StorageID) | |||||
| loadByID(cmdCtx, pkg.PackageID, stg.StorageID, rootPath) | |||||
| } | } | ||||
| func loadByID(cmdCtx *CommandContext, pkgID cdssdk.PackageID, stgID cdssdk.StorageID) { | |||||
| func loadByID(cmdCtx *CommandContext, pkgID cdssdk.PackageID, stgID cdssdk.StorageID, rootPath string) { | |||||
| userID := cdssdk.UserID(1) | userID := cdssdk.UserID(1) | ||||
| startTime := time.Now() | startTime := time.Now() | ||||
| hubID, taskID, err := cmdCtx.Cmdline.Svc.StorageSvc().StartStorageLoadPackage(userID, pkgID, stgID) | |||||
| err := cmdCtx.Cmdline.Svc.StorageSvc().LoadPackage(userID, pkgID, stgID, rootPath) | |||||
| if err != nil { | if err != nil { | ||||
| fmt.Println(err) | fmt.Println(err) | ||||
| return | return | ||||
| } | } | ||||
| for { | |||||
| complete, fullPath, err := cmdCtx.Cmdline.Svc.StorageSvc().WaitStorageLoadPackage(hubID, taskID, time.Second*10) | |||||
| if err != nil { | |||||
| fmt.Println(err) | |||||
| return | |||||
| } | |||||
| if complete { | |||||
| fmt.Printf("Package loaded to: %s in %v\n", fullPath, time.Since(startTime)) | |||||
| break | |||||
| } | |||||
| } | |||||
| fmt.Printf("Package loaded to: %v:%v in %v\n", stgID, rootPath, time.Since(startTime)) | |||||
| } | } | ||||
| @@ -46,7 +46,7 @@ func lspByPath(cmdCtx *CommandContext, path string) { | |||||
| return | return | ||||
| } | } | ||||
| pkg, err := cmdCtx.Cmdline.Svc.PackageSvc().GetByName(userID, comps[0], comps[1]) | |||||
| pkg, err := cmdCtx.Cmdline.Svc.PackageSvc().GetByFullName(userID, comps[0], comps[1]) | |||||
| if err != nil { | if err != nil { | ||||
| fmt.Println(err) | fmt.Println(err) | ||||
| return | return | ||||
| @@ -29,26 +29,34 @@ func init() { | |||||
| packageName := args[2] | packageName := args[2] | ||||
| storageIDs := make([]cdssdk.StorageID, 0) | storageIDs := make([]cdssdk.StorageID, 0) | ||||
| for _, sID := range args[3:] { | |||||
| sID, err := strconv.ParseInt(sID, 10, 64) | |||||
| rootPathes := make([]string, 0) | |||||
| for _, dst := range args[3:] { | |||||
| comps := strings.Split(dst, ":") | |||||
| if len(comps) != 2 { | |||||
| fmt.Println("invalid storage destination: ", dst) | |||||
| return | |||||
| } | |||||
| sID, err := strconv.ParseInt(comps[0], 10, 64) | |||||
| if err != nil { | if err != nil { | ||||
| fmt.Println(err) | fmt.Println(err) | ||||
| return | return | ||||
| } | } | ||||
| storageIDs = append(storageIDs, cdssdk.StorageID(sID)) | storageIDs = append(storageIDs, cdssdk.StorageID(sID)) | ||||
| rootPathes = append(rootPathes, comps[1]) | |||||
| } | } | ||||
| newloadp(cmdCtx, localPath, cdssdk.BucketID(bktID), packageName, storageIDs) | |||||
| newloadp(cmdCtx, localPath, cdssdk.BucketID(bktID), packageName, storageIDs, rootPathes) | |||||
| }, | }, | ||||
| } | } | ||||
| rootCmd.AddCommand(cmd) | rootCmd.AddCommand(cmd) | ||||
| } | } | ||||
| func newloadp(cmdCtx *CommandContext, path string, bucketID cdssdk.BucketID, packageName string, storageIDs []cdssdk.StorageID) { | |||||
| func newloadp(cmdCtx *CommandContext, path string, bucketID cdssdk.BucketID, packageName string, storageIDs []cdssdk.StorageID, rootPathes []string) { | |||||
| userID := cdssdk.UserID(1) | userID := cdssdk.UserID(1) | ||||
| up, err := cmdCtx.Cmdline.Svc.Uploader.BeginCreateLoad(userID, bucketID, packageName, storageIDs) | |||||
| up, err := cmdCtx.Cmdline.Svc.Uploader.BeginCreateLoad(userID, bucketID, packageName, storageIDs, rootPathes) | |||||
| if err != nil { | if err != nil { | ||||
| fmt.Println(err) | fmt.Println(err) | ||||
| return | return | ||||
| @@ -94,7 +102,7 @@ func newloadp(cmdCtx *CommandContext, path string, bucketID cdssdk.BucketID, pac | |||||
| } | } | ||||
| wr := table.NewWriter() | wr := table.NewWriter() | ||||
| wr.AppendHeader(table.Row{"ID", "Name", "FileCount", "TotalSize", "LoadedDirs"}) | |||||
| wr.AppendRow(table.Row{ret.Package.PackageID, ret.Package.Name, fileCount, totalSize, strings.Join(ret.LoadedDirs, "\n")}) | |||||
| wr.AppendHeader(table.Row{"ID", "Name", "FileCount", "TotalSize"}) | |||||
| wr.AppendRow(table.Row{ret.Package.PackageID, ret.Package.Name, fileCount, totalSize}) | |||||
| fmt.Println(wr.Render()) | fmt.Println(wr.Render()) | ||||
| } | } | ||||
| @@ -33,7 +33,7 @@ var _ = MustAddCmd(func(ctx CommandContext, packageID cdssdk.PackageID, rootPath | |||||
| storageAff = storageAffinity[0] | storageAff = storageAffinity[0] | ||||
| } | } | ||||
| up, err := ctx.Cmdline.Svc.Uploader.BeginUpdate(userID, packageID, storageAff) | |||||
| up, err := ctx.Cmdline.Svc.Uploader.BeginUpdate(userID, packageID, storageAff, nil, nil) | |||||
| if err != nil { | if err != nil { | ||||
| return fmt.Errorf("begin updating package: %w", err) | return fmt.Errorf("begin updating package: %w", err) | ||||
| } | } | ||||
| @@ -181,26 +181,6 @@ func PackageGetCachedStorages(ctx CommandContext, packageID cdssdk.PackageID) er | |||||
| return nil | return nil | ||||
| } | } | ||||
| // PackageGetLoadedStorages 获取指定包裹的已加载节点信息。 | |||||
| // | |||||
| // 参数: | |||||
| // | |||||
| // ctx - 命令上下文。 | |||||
| // packageID - 包裹ID。 | |||||
| // | |||||
| // 返回值: | |||||
| // | |||||
| // error - 操作过程中发生的任何错误。 | |||||
| func PackageGetLoadedStorages(ctx CommandContext, packageID cdssdk.PackageID) error { | |||||
| userID := cdssdk.UserID(1) | |||||
| hubIDs, err := ctx.Cmdline.Svc.PackageSvc().GetLoadedStorages(userID, packageID) | |||||
| fmt.Printf("hubIDs: %v\n", hubIDs) | |||||
| if err != nil { | |||||
| return fmt.Errorf("get package %d loaded storages failed, err: %w", packageID, err) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| // 初始化命令行工具的包相关命令。 | // 初始化命令行工具的包相关命令。 | ||||
| func init() { | func init() { | ||||
| commands.MustAdd(PackageListBucketPackages, "pkg", "ls") | commands.MustAdd(PackageListBucketPackages, "pkg", "ls") | ||||
| @@ -213,7 +193,4 @@ func init() { | |||||
| // 查询package缓存到哪些节点 | // 查询package缓存到哪些节点 | ||||
| commands.MustAdd(PackageGetCachedStorages, "pkg", "cached") | commands.MustAdd(PackageGetCachedStorages, "pkg", "cached") | ||||
| // 查询package调度到哪些节点 | |||||
| commands.MustAdd(PackageGetLoadedStorages, "pkg", "loaded") | |||||
| } | } | ||||
| @@ -48,7 +48,7 @@ func init() { | |||||
| return | return | ||||
| } | } | ||||
| pkg, err := cmdCtx.Cmdline.Svc.PackageSvc().GetByName(userID, comps[0], comps[1]) | |||||
| pkg, err := cmdCtx.Cmdline.Svc.PackageSvc().GetByFullName(userID, comps[0], comps[1]) | |||||
| if err != nil { | if err != nil { | ||||
| if codeMsg, ok := err.(*mq.CodeMessageError); ok && codeMsg.Code == errorcode.DataNotFound { | if codeMsg, ok := err.(*mq.CodeMessageError); ok && codeMsg.Code == errorcode.DataNotFound { | ||||
| pkg2, err := cmdCtx.Cmdline.Svc.PackageSvc().Create(userID, bkt.BucketID, comps[1]) | pkg2, err := cmdCtx.Cmdline.Svc.PackageSvc().Create(userID, bkt.BucketID, comps[1]) | ||||
| @@ -68,7 +68,7 @@ func init() { | |||||
| storageAff = cdssdk.StorageID(stgID) | storageAff = cdssdk.StorageID(stgID) | ||||
| } | } | ||||
| up, err := cmdCtx.Cmdline.Svc.Uploader.BeginUpdate(userID, pkg.PackageID, storageAff) | |||||
| up, err := cmdCtx.Cmdline.Svc.Uploader.BeginUpdate(userID, pkg.PackageID, storageAff, nil, nil) | |||||
| if err != nil { | if err != nil { | ||||
| fmt.Printf("begin updating package: %v\n", err) | fmt.Printf("begin updating package: %v\n", err) | ||||
| return | return | ||||
| @@ -3,6 +3,7 @@ package cmdline | |||||
| import ( | import ( | ||||
| "fmt" | "fmt" | ||||
| "gitlink.org.cn/cloudream/storage/client/internal/config" | |||||
| "gitlink.org.cn/cloudream/storage/client/internal/http" | "gitlink.org.cn/cloudream/storage/client/internal/http" | ||||
| ) | ) | ||||
| @@ -17,8 +18,13 @@ func ServeHTTP(ctx CommandContext, args []string) error { | |||||
| listenAddr = args[0] | listenAddr = args[0] | ||||
| } | } | ||||
| awsAuth, err := http.NewAWSAuth(config.Cfg().AuthAccessKey, config.Cfg().AuthSecretKey) | |||||
| if err != nil { | |||||
| return fmt.Errorf("new aws auth: %w", err) | |||||
| } | |||||
| // 创建一个新的HTTP服务器实例。 | // 创建一个新的HTTP服务器实例。 | ||||
| httpSvr, err := http.NewServer(listenAddr, ctx.Cmdline.Svc) | |||||
| httpSvr, err := http.NewServer(listenAddr, ctx.Cmdline.Svc, awsAuth) | |||||
| if err != nil { | if err != nil { | ||||
| return fmt.Errorf("new http server: %w", err) | return fmt.Errorf("new http server: %w", err) | ||||
| } | } | ||||
| @@ -7,42 +7,6 @@ import ( | |||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | ||||
| ) | ) | ||||
| // StorageLoadPackage 加载指定的包到存储系统中。 | |||||
| // ctx: 命令上下文,提供必要的服务和环境配置。 | |||||
| // packageID: 需要加载的包的唯一标识。 | |||||
| // storageID: 目标存储系统的唯一标识。 | |||||
| // 返回值: 执行过程中遇到的任何错误。 | |||||
| func StorageLoadPackage(ctx CommandContext, packageID cdssdk.PackageID, storageID cdssdk.StorageID) error { | |||||
| startTime := time.Now() | |||||
| defer func() { | |||||
| // 打印函数执行时间 | |||||
| fmt.Printf("%v\n", time.Since(startTime).Seconds()) | |||||
| }() | |||||
| // 开始加载包到存储系统 | |||||
| hubID, taskID, err := ctx.Cmdline.Svc.StorageSvc().StartStorageLoadPackage(1, packageID, storageID) | |||||
| if err != nil { | |||||
| return fmt.Errorf("start loading package to storage: %w", err) | |||||
| } | |||||
| // 循环等待加载完成 | |||||
| for { | |||||
| complete, fullPath, err := ctx.Cmdline.Svc.StorageSvc().WaitStorageLoadPackage(hubID, taskID, time.Second*10) | |||||
| if complete { | |||||
| if err != nil { | |||||
| return fmt.Errorf("moving complete with: %w", err) | |||||
| } | |||||
| fmt.Printf("Load To: %s\n", fullPath) | |||||
| return nil | |||||
| } | |||||
| if err != nil { | |||||
| return fmt.Errorf("wait moving: %w", err) | |||||
| } | |||||
| } | |||||
| } | |||||
| // StorageCreatePackage 创建一个新的包并上传到指定的存储系统。 | // StorageCreatePackage 创建一个新的包并上传到指定的存储系统。 | ||||
| // ctx: 命令上下文,提供必要的服务和环境配置。 | // ctx: 命令上下文,提供必要的服务和环境配置。 | ||||
| // bucketID: 存储桶的唯一标识,包将被上传到这个存储桶中。 | // bucketID: 存储桶的唯一标识,包将被上传到这个存储桶中。 | ||||
| @@ -83,9 +47,6 @@ func StorageCreatePackage(ctx CommandContext, bucketID cdssdk.BucketID, name str | |||||
| // 初始化函数,注册加载包和创建包的命令到命令行解析器。 | // 初始化函数,注册加载包和创建包的命令到命令行解析器。 | ||||
| func init() { | func init() { | ||||
| // 注册加载包命令 | |||||
| commands.MustAdd(StorageLoadPackage, "stg", "pkg", "load") | |||||
| // 注册创建包命令 | // 注册创建包命令 | ||||
| commands.MustAdd(StorageCreatePackage, "stg", "pkg", "new") | commands.MustAdd(StorageCreatePackage, "stg", "pkg", "new") | ||||
| } | } | ||||
| @@ -9,6 +9,7 @@ import ( | |||||
| "gitlink.org.cn/cloudream/common/pkgs/future" | "gitlink.org.cn/cloudream/common/pkgs/future" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | ||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | ||||
| "gitlink.org.cn/cloudream/common/utils/math2" | |||||
| stgglb "gitlink.org.cn/cloudream/storage/common/globals" | stgglb "gitlink.org.cn/cloudream/storage/common/globals" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2" | "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/parser" | "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/parser" | ||||
| @@ -26,20 +27,20 @@ func init() { | |||||
| } | } | ||||
| defer stgglb.CoordinatorMQPool.Release(coorCli) | defer stgglb.CoordinatorMQPool.Release(coorCli) | ||||
| stgs, err := coorCli.GetStorageDetails(coormq.ReqGetStorageDetails([]cdssdk.StorageID{1, 2, 3})) | |||||
| stgs, err := coorCli.GetStorageDetails(coormq.ReqGetStorageDetails([]cdssdk.StorageID{1, 2, 3, 4})) | |||||
| if err != nil { | if err != nil { | ||||
| panic(err) | panic(err) | ||||
| } | } | ||||
| ft := ioswitch2.NewFromTo() | ft := ioswitch2.NewFromTo() | ||||
| ft.SegmentParam = cdssdk.NewSegmentRedundancy(1024*100*3, 3) | ft.SegmentParam = cdssdk.NewSegmentRedundancy(1024*100*3, 3) | ||||
| ft.AddFrom(ioswitch2.NewFromShardstore("FullE58B075E9F7C5744CB1C2CBBECC30F163DE699DCDA94641DDA34A0C2EB01E240", *stgs.Storages[1].MasterHub, stgs.Storages[1].Storage, ioswitch2.SegmentStream(0))) | |||||
| ft.AddFrom(ioswitch2.NewFromShardstore("FullEA14D17544786427C3A766F0C5E6DEB221D00D3DE1875BBE3BD0AD5C8118C1A0", *stgs.Storages[1].MasterHub, stgs.Storages[1].Storage, ioswitch2.SegmentStream(1))) | |||||
| ft.AddFrom(ioswitch2.NewFromShardstore("Full4D142C458F2399175232D5636235B09A84664D60869E925EB20FFBE931045BDD", *stgs.Storages[1].MasterHub, stgs.Storages[1].Storage, ioswitch2.SegmentStream(2))) | |||||
| ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[2].MasterHub, *stgs.Storages[2], ioswitch2.RawStream(), "0")) | |||||
| // ft.AddFrom(ioswitch2.NewFromShardstore("CA56E5934859E0220D1F3B848F41619D937D7B874D4EBF63A6CC98D2D8E3280F", *stgs.Storages[0].MasterHub, stgs.Storages[0].Storage, ioswitch2.RawStream())) | |||||
| // ft.AddFrom(ioswitch2.NewFromShardstore("FullE58B075E9F7C5744CB1C2CBBECC30F163DE699DCDA94641DDA34A0C2EB01E240", *stgs.Storages[1].MasterHub, *stgs.Storages[1], ioswitch2.SegmentStream(0))) | |||||
| // ft.AddFrom(ioswitch2.NewFromShardstore("FullEA14D17544786427C3A766F0C5E6DEB221D00D3DE1875BBE3BD0AD5C8118C1A0", *stgs.Storages[1].MasterHub, *stgs.Storages[1], ioswitch2.SegmentStream(1))) | |||||
| // ft.AddFrom(ioswitch2.NewFromShardstore("Full4D142C458F2399175232D5636235B09A84664D60869E925EB20FFBE931045BDD", *stgs.Storages[1].MasterHub, *stgs.Storages[1], ioswitch2.SegmentStream(2))) | |||||
| ft.AddFrom(ioswitch2.NewFromShardstore("Full03B5CF4B57251D7BB4308FE5C81AF5A21E2B28994CC7CB1FB37698DAE271DC22", *stgs.Storages[2].MasterHub, *stgs.Storages[2], ioswitch2.RawStream())) | |||||
| ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[3].MasterHub, *stgs.Storages[3], ioswitch2.RawStream(), "0")) | |||||
| // ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[1].MasterHub, stgs.Storages[1].Storage, ioswitch2.SegmentStream(0), "0")) | // ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[1].MasterHub, stgs.Storages[1].Storage, ioswitch2.SegmentStream(0), "0")) | ||||
| // ft.AddTo(ioswitch2.NewToShardStoreWithRange(*stgs.Storages[1].MasterHub, *stgs.Storages[1], ioswitch2.SegmentStream(1), "1", exec.Range{Offset: 1})) | |||||
| // ft.AddTo(ioswitch2.NewToShardStoreWithRange(*stgs.Storages[1].MasterHub, *stgs.Storages[1], ioswitch2.SegmentStream(1), "1", math2.Range{Offset: 1})) | |||||
| // ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[1].MasterHub, *stgs.Storages[1], ioswitch2.SegmentStream(0), "0")) | // ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[1].MasterHub, *stgs.Storages[1], ioswitch2.SegmentStream(0), "0")) | ||||
| // ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[1].MasterHub, *stgs.Storages[1], ioswitch2.SegmentStream(1), "1")) | // ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[1].MasterHub, *stgs.Storages[1], ioswitch2.SegmentStream(1), "1")) | ||||
| // ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[1].MasterHub, *stgs.Storages[1], ioswitch2.SegmentStream(2), "2")) | // ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[1].MasterHub, *stgs.Storages[1], ioswitch2.SegmentStream(2), "2")) | ||||
| @@ -86,9 +87,9 @@ func init() { | |||||
| ft := ioswitch2.NewFromTo() | ft := ioswitch2.NewFromTo() | ||||
| ft.SegmentParam = cdssdk.NewSegmentRedundancy(1293, 3) | ft.SegmentParam = cdssdk.NewSegmentRedundancy(1293, 3) | ||||
| ft.AddFrom(ioswitch2.NewFromShardstore("4E69A8B8CD9F42EDE371DA94458BADFB2308AFCA736AA393784A3D81F4746377", *stgs.Storages[0].MasterHub, stgs.Storages[0].Storage, ioswitch2.RawStream())) | |||||
| ft.AddFrom(ioswitch2.NewFromShardstore("4E69A8B8CD9F42EDE371DA94458BADFB2308AFCA736AA393784A3D81F4746377", *stgs.Storages[0].MasterHub, *stgs.Storages[0], ioswitch2.RawStream())) | |||||
| // ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[1].MasterHub, stgs.Storages[1].Storage, ioswitch2.SegmentStream(0), "0")) | // ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[1].MasterHub, stgs.Storages[1].Storage, ioswitch2.SegmentStream(0), "0")) | ||||
| ft.AddTo(ioswitch2.NewToShardStoreWithRange(*stgs.Storages[1].MasterHub, *stgs.Storages[1], ioswitch2.SegmentStream(1), "1", exec.Range{Offset: 1})) | |||||
| ft.AddTo(ioswitch2.NewToShardStoreWithRange(*stgs.Storages[1].MasterHub, *stgs.Storages[1], ioswitch2.SegmentStream(1), "1", math2.Range{Offset: 1})) | |||||
| ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[1].MasterHub, *stgs.Storages[1], ioswitch2.SegmentStream(2), "2")) | ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[1].MasterHub, *stgs.Storages[1], ioswitch2.SegmentStream(2), "2")) | ||||
| plans := exec.NewPlanBuilder() | plans := exec.NewPlanBuilder() | ||||
| @@ -133,15 +134,15 @@ func init() { | |||||
| ft := ioswitch2.NewFromTo() | ft := ioswitch2.NewFromTo() | ||||
| ft.SegmentParam = cdssdk.NewSegmentRedundancy(1293, 3) | ft.SegmentParam = cdssdk.NewSegmentRedundancy(1293, 3) | ||||
| ft.ECParam = &cdssdk.DefaultECRedundancy | ft.ECParam = &cdssdk.DefaultECRedundancy | ||||
| ft.AddFrom(ioswitch2.NewFromShardstore("22CC59CE3297F78F2D20DC1E33181B77F21E6782097C94E1664F99F129834069", *stgs.Storages[1].MasterHub, stgs.Storages[1].Storage, ioswitch2.SegmentStream(0))) | |||||
| ft.AddFrom(ioswitch2.NewFromShardstore("5EAC20EB3EBC7B5FA176C5BD1C01041FB2A6D14C35D6A232CA83D7F1E4B01ADE", *stgs.Storages[1].MasterHub, stgs.Storages[1].Storage, ioswitch2.SegmentStream(1))) | |||||
| ft.AddFrom(ioswitch2.NewFromShardstore("A9BC1802F37100C80C72A1D6E8F53C0E0B73F85F99153D8C78FB01CEC9D8D903", *stgs.Storages[1].MasterHub, stgs.Storages[1].Storage, ioswitch2.SegmentStream(2))) | |||||
| ft.AddFrom(ioswitch2.NewFromShardstore("22CC59CE3297F78F2D20DC1E33181B77F21E6782097C94E1664F99F129834069", *stgs.Storages[1].MasterHub, *stgs.Storages[1], ioswitch2.SegmentStream(0))) | |||||
| ft.AddFrom(ioswitch2.NewFromShardstore("5EAC20EB3EBC7B5FA176C5BD1C01041FB2A6D14C35D6A232CA83D7F1E4B01ADE", *stgs.Storages[1].MasterHub, *stgs.Storages[1], ioswitch2.SegmentStream(1))) | |||||
| ft.AddFrom(ioswitch2.NewFromShardstore("A9BC1802F37100C80C72A1D6E8F53C0E0B73F85F99153D8C78FB01CEC9D8D903", *stgs.Storages[1].MasterHub, *stgs.Storages[1], ioswitch2.SegmentStream(2))) | |||||
| toDrv, drvStr := ioswitch2.NewToDriverWithRange(ioswitch2.RawStream(), exec.NewRange(0, 1293)) | |||||
| toDrv, drvStr := ioswitch2.NewToDriverWithRange(ioswitch2.RawStream(), math2.NewRange(0, 1293)) | |||||
| ft.AddTo(toDrv) | ft.AddTo(toDrv) | ||||
| ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[0].MasterHub, *stgs.Storages[0], ioswitch2.ECSrteam(0), "EC0")) | |||||
| ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[0].MasterHub, *stgs.Storages[0], ioswitch2.ECSrteam(1), "EC1")) | |||||
| ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[0].MasterHub, *stgs.Storages[0], ioswitch2.ECSrteam(2), "EC2")) | |||||
| ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[0].MasterHub, *stgs.Storages[0], ioswitch2.ECStream(0), "EC0")) | |||||
| ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[0].MasterHub, *stgs.Storages[0], ioswitch2.ECStream(1), "EC1")) | |||||
| ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[0].MasterHub, *stgs.Storages[0], ioswitch2.ECStream(2), "EC2")) | |||||
| plans := exec.NewPlanBuilder() | plans := exec.NewPlanBuilder() | ||||
| err = parser.Parse(ft, plans) | err = parser.Parse(ft, plans) | ||||
| @@ -202,10 +203,10 @@ func init() { | |||||
| ft := ioswitch2.NewFromTo() | ft := ioswitch2.NewFromTo() | ||||
| ft.ECParam = &cdssdk.DefaultECRedundancy | ft.ECParam = &cdssdk.DefaultECRedundancy | ||||
| ft.AddFrom(ioswitch2.NewFromShardstore("4E69A8B8CD9F42EDE371DA94458BADFB2308AFCA736AA393784A3D81F4746377", *stgs.Storages[1].MasterHub, stgs.Storages[1].Storage, ioswitch2.RawStream())) | |||||
| ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[0].MasterHub, *stgs.Storages[0], ioswitch2.ECSrteam(0), "EC0")) | |||||
| ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[0].MasterHub, *stgs.Storages[0], ioswitch2.ECSrteam(1), "EC1")) | |||||
| ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[0].MasterHub, *stgs.Storages[0], ioswitch2.ECSrteam(2), "EC2")) | |||||
| ft.AddFrom(ioswitch2.NewFromShardstore("4E69A8B8CD9F42EDE371DA94458BADFB2308AFCA736AA393784A3D81F4746377", *stgs.Storages[1].MasterHub, *stgs.Storages[1], ioswitch2.RawStream())) | |||||
| ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[0].MasterHub, *stgs.Storages[0], ioswitch2.ECStream(0), "EC0")) | |||||
| ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[0].MasterHub, *stgs.Storages[0], ioswitch2.ECStream(1), "EC1")) | |||||
| ft.AddTo(ioswitch2.NewToShardStore(*stgs.Storages[0].MasterHub, *stgs.Storages[0], ioswitch2.ECStream(2), "EC2")) | |||||
| plans := exec.NewPlanBuilder() | plans := exec.NewPlanBuilder() | ||||
| err = parser.Parse(ft, plans) | err = parser.Parse(ft, plans) | ||||
| @@ -253,10 +254,10 @@ func init() { | |||||
| ft := ioswitch2.NewFromTo() | ft := ioswitch2.NewFromTo() | ||||
| ft.SegmentParam = cdssdk.NewSegmentRedundancy(1293, 3) | ft.SegmentParam = cdssdk.NewSegmentRedundancy(1293, 3) | ||||
| ft.ECParam = &cdssdk.DefaultECRedundancy | ft.ECParam = &cdssdk.DefaultECRedundancy | ||||
| ft.AddFrom(ioswitch2.NewFromShardstore("22CC59CE3297F78F2D20DC1E33181B77F21E6782097C94E1664F99F129834069", *stgs.Storages[1].MasterHub, stgs.Storages[1].Storage, ioswitch2.SegmentStream(0))) | |||||
| ft.AddFrom(ioswitch2.NewFromShardstore("5EAC20EB3EBC7B5FA176C5BD1C01041FB2A6D14C35D6A232CA83D7F1E4B01ADE", *stgs.Storages[1].MasterHub, stgs.Storages[1].Storage, ioswitch2.SegmentStream(1))) | |||||
| ft.AddFrom(ioswitch2.NewFromShardstore("A9BC1802F37100C80C72A1D6E8F53C0E0B73F85F99153D8C78FB01CEC9D8D903", *stgs.Storages[1].MasterHub, stgs.Storages[1].Storage, ioswitch2.SegmentStream(2))) | |||||
| ft.AddTo(ioswitch2.NewToShardStoreWithRange(*stgs.Storages[0].MasterHub, *stgs.Storages[0], ioswitch2.RawStream(), "raw", exec.NewRange(10, 645))) | |||||
| ft.AddFrom(ioswitch2.NewFromShardstore("22CC59CE3297F78F2D20DC1E33181B77F21E6782097C94E1664F99F129834069", *stgs.Storages[1].MasterHub, *stgs.Storages[1], ioswitch2.SegmentStream(0))) | |||||
| ft.AddFrom(ioswitch2.NewFromShardstore("5EAC20EB3EBC7B5FA176C5BD1C01041FB2A6D14C35D6A232CA83D7F1E4B01ADE", *stgs.Storages[1].MasterHub, *stgs.Storages[1], ioswitch2.SegmentStream(1))) | |||||
| ft.AddFrom(ioswitch2.NewFromShardstore("A9BC1802F37100C80C72A1D6E8F53C0E0B73F85F99153D8C78FB01CEC9D8D903", *stgs.Storages[1].MasterHub, *stgs.Storages[1], ioswitch2.SegmentStream(2))) | |||||
| ft.AddTo(ioswitch2.NewToShardStoreWithRange(*stgs.Storages[0].MasterHub, *stgs.Storages[0], ioswitch2.RawStream(), "raw", math2.NewRange(10, 645))) | |||||
| plans := exec.NewPlanBuilder() | plans := exec.NewPlanBuilder() | ||||
| err = parser.Parse(ft, plans) | err = parser.Parse(ft, plans) | ||||
| @@ -3,24 +3,29 @@ package config | |||||
| import ( | import ( | ||||
| "gitlink.org.cn/cloudream/common/pkgs/distlock" | "gitlink.org.cn/cloudream/common/pkgs/distlock" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/logger" | "gitlink.org.cn/cloudream/common/pkgs/logger" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/mq" | |||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | ||||
| "gitlink.org.cn/cloudream/common/utils/config" | "gitlink.org.cn/cloudream/common/utils/config" | ||||
| stgmodels "gitlink.org.cn/cloudream/storage/common/models" | stgmodels "gitlink.org.cn/cloudream/storage/common/models" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/connectivity" | "gitlink.org.cn/cloudream/storage/common/pkgs/connectivity" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/downloader" | "gitlink.org.cn/cloudream/storage/common/pkgs/downloader" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/downloader/strategy" | |||||
| agtrpc "gitlink.org.cn/cloudream/storage/common/pkgs/grpc/agent" | agtrpc "gitlink.org.cn/cloudream/storage/common/pkgs/grpc/agent" | ||||
| stgmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq" | |||||
| ) | ) | ||||
| type Config struct { | type Config struct { | ||||
| Local stgmodels.LocalMachineInfo `json:"local"` | |||||
| AgentGRPC agtrpc.PoolConfig `json:"agentGRPC"` | |||||
| Logger logger.Config `json:"logger"` | |||||
| RabbitMQ stgmq.Config `json:"rabbitMQ"` | |||||
| DistLock distlock.Config `json:"distlock"` | |||||
| Connectivity connectivity.Config `json:"connectivity"` | |||||
| Downloader downloader.Config `json:"downloader"` | |||||
| StorageID cdssdk.StorageID `json:"storageID"` // TODO 进行访问量统计时,当前客户端所属的存储ID。临时解决方案。 | |||||
| Local stgmodels.LocalMachineInfo `json:"local"` | |||||
| AgentGRPC agtrpc.PoolConfig `json:"agentGRPC"` | |||||
| Logger logger.Config `json:"logger"` | |||||
| RabbitMQ mq.Config `json:"rabbitMQ"` | |||||
| DistLock distlock.Config `json:"distlock"` | |||||
| Connectivity connectivity.Config `json:"connectivity"` | |||||
| Downloader downloader.Config `json:"downloader"` | |||||
| DownloadStrategy strategy.Config `json:"downloadStrategy"` | |||||
| StorageID cdssdk.StorageID `json:"storageID"` // TODO 进行访问量统计时,当前客户端所属的存储ID。临时解决方案。 | |||||
| AuthAccessKey string `json:"authAccessKey"` // TODO 临时办法 | |||||
| AuthSecretKey string `json:"authSecretKey"` | |||||
| MaxHTTPBodySize int64 `json:"maxHttpBodySize"` | |||||
| } | } | ||||
| var cfg Config | var cfg Config | ||||
| @@ -0,0 +1,197 @@ | |||||
| package http | |||||
| import ( | |||||
| "bytes" | |||||
| "context" | |||||
| "crypto/sha256" | |||||
| "encoding/hex" | |||||
| "fmt" | |||||
| "io" | |||||
| "net/http" | |||||
| "strings" | |||||
| "time" | |||||
| "github.com/aws/aws-sdk-go-v2/aws" | |||||
| v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4" | |||||
| "github.com/aws/aws-sdk-go-v2/credentials" | |||||
| "github.com/gin-gonic/gin" | |||||
| "gitlink.org.cn/cloudream/common/consts/errorcode" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/logger" | |||||
| "gitlink.org.cn/cloudream/storage/client/internal/config" | |||||
| ) | |||||
| const ( | |||||
| AuthRegion = "any" | |||||
| AuthService = "jcs" | |||||
| AuthorizationHeader = "Authorization" | |||||
| ) | |||||
| type AWSAuth struct { | |||||
| cred aws.Credentials | |||||
| signer *v4.Signer | |||||
| } | |||||
| func NewAWSAuth(accessKey string, secretKey string) (*AWSAuth, error) { | |||||
| prod := credentials.NewStaticCredentialsProvider(accessKey, secretKey, "") | |||||
| cred, err := prod.Retrieve(context.TODO()) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| return &AWSAuth{ | |||||
| cred: cred, | |||||
| signer: v4.NewSigner(), | |||||
| }, nil | |||||
| } | |||||
| func (a *AWSAuth) Auth(c *gin.Context) { | |||||
| authorizationHeader := c.GetHeader(AuthorizationHeader) | |||||
| if authorizationHeader == "" { | |||||
| c.AbortWithStatusJSON(http.StatusBadRequest, Failed(errorcode.Unauthorized, "authorization header is missing")) | |||||
| return | |||||
| } | |||||
| _, headers, reqSig, err := parseAuthorizationHeader(authorizationHeader) | |||||
| if err != nil { | |||||
| c.AbortWithStatusJSON(http.StatusBadRequest, Failed(errorcode.Unauthorized, "invalid Authorization header format")) | |||||
| return | |||||
| } | |||||
| // 限制请求体大小 | |||||
| rd := io.LimitReader(c.Request.Body, config.Cfg().MaxHTTPBodySize) | |||||
| body, err := io.ReadAll(rd) | |||||
| if err != nil { | |||||
| c.AbortWithStatusJSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "read request body failed")) | |||||
| return | |||||
| } | |||||
| payloadHash := sha256.Sum256(body) | |||||
| hexPayloadHash := hex.EncodeToString(payloadHash[:]) | |||||
| // 构造验签用的请求 | |||||
| verifyReq, err := http.NewRequest(c.Request.Method, c.Request.URL.String(), nil) | |||||
| if err != nil { | |||||
| c.AbortWithStatusJSON(http.StatusOK, Failed(errorcode.OperationFailed, err.Error())) | |||||
| return | |||||
| } | |||||
| for _, h := range headers { | |||||
| verifyReq.Header.Add(h, c.Request.Header.Get(h)) | |||||
| } | |||||
| verifyReq.Host = c.Request.Host | |||||
| timestamp, err := time.Parse("20060102T150405Z", c.GetHeader("X-Amz-Date")) | |||||
| if err != nil { | |||||
| c.AbortWithStatusJSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "invalid X-Amz-Date header format")) | |||||
| return | |||||
| } | |||||
| signer := v4.NewSigner() | |||||
| err = signer.SignHTTP(context.TODO(), a.cred, verifyReq, hexPayloadHash, AuthService, AuthRegion, timestamp) | |||||
| if err != nil { | |||||
| logger.Warnf("sign request: %v", err) | |||||
| c.AbortWithStatusJSON(http.StatusOK, Failed(errorcode.OperationFailed, "sign request failed")) | |||||
| return | |||||
| } | |||||
| verifySig := a.getSignature(verifyReq) | |||||
| if !strings.EqualFold(verifySig, reqSig) { | |||||
| c.AbortWithStatusJSON(http.StatusOK, Failed(errorcode.Unauthorized, "signature mismatch")) | |||||
| return | |||||
| } | |||||
| c.Request.Body = io.NopCloser(bytes.NewReader(body)) | |||||
| c.Next() | |||||
| } | |||||
| func (a *AWSAuth) AuthWithoutBody(c *gin.Context) { | |||||
| authorizationHeader := c.GetHeader(AuthorizationHeader) | |||||
| if authorizationHeader == "" { | |||||
| c.AbortWithStatusJSON(http.StatusBadRequest, Failed(errorcode.Unauthorized, "authorization header is missing")) | |||||
| return | |||||
| } | |||||
| _, headers, reqSig, err := parseAuthorizationHeader(authorizationHeader) | |||||
| if err != nil { | |||||
| c.AbortWithStatusJSON(http.StatusBadRequest, Failed(errorcode.Unauthorized, "invalid Authorization header format")) | |||||
| return | |||||
| } | |||||
| // 构造验签用的请求 | |||||
| verifyReq, err := http.NewRequest(c.Request.Method, c.Request.URL.String(), nil) | |||||
| if err != nil { | |||||
| c.AbortWithStatusJSON(http.StatusOK, Failed(errorcode.OperationFailed, err.Error())) | |||||
| return | |||||
| } | |||||
| for _, h := range headers { | |||||
| verifyReq.Header.Add(h, c.Request.Header.Get(h)) | |||||
| } | |||||
| verifyReq.Host = c.Request.Host | |||||
| timestamp, err := time.Parse("20060102T150405Z", c.GetHeader("X-Amz-Date")) | |||||
| if err != nil { | |||||
| c.AbortWithStatusJSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "invalid X-Amz-Date header format")) | |||||
| return | |||||
| } | |||||
| err = a.signer.SignHTTP(context.TODO(), a.cred, verifyReq, "", AuthService, AuthRegion, timestamp) | |||||
| if err != nil { | |||||
| logger.Warnf("sign request: %v", err) | |||||
| c.AbortWithStatusJSON(http.StatusOK, Failed(errorcode.OperationFailed, "sign request failed")) | |||||
| return | |||||
| } | |||||
| verifySig := a.getSignature(verifyReq) | |||||
| if strings.EqualFold(verifySig, reqSig) { | |||||
| c.AbortWithStatusJSON(http.StatusOK, Failed(errorcode.Unauthorized, "signature mismatch")) | |||||
| return | |||||
| } | |||||
| c.Next() | |||||
| } | |||||
| // 解析 Authorization 头部 | |||||
| func parseAuthorizationHeader(authorizationHeader string) (string, []string, string, error) { | |||||
| if !strings.HasPrefix(authorizationHeader, "AWS4-HMAC-SHA256 ") { | |||||
| return "", nil, "", fmt.Errorf("invalid Authorization header format") | |||||
| } | |||||
| authorizationHeader = strings.TrimPrefix(authorizationHeader, "AWS4-HMAC-SHA256") | |||||
| parts := strings.Split(authorizationHeader, ",") | |||||
| if len(parts) != 3 { | |||||
| return "", nil, "", fmt.Errorf("invalid Authorization header format") | |||||
| } | |||||
| var credential, signedHeaders, signature string | |||||
| for _, part := range parts { | |||||
| part = strings.TrimSpace(part) | |||||
| if strings.HasPrefix(part, "Credential=") { | |||||
| credential = strings.TrimPrefix(part, "Credential=") | |||||
| } | |||||
| if strings.HasPrefix(part, "SignedHeaders=") { | |||||
| signedHeaders = strings.TrimPrefix(part, "SignedHeaders=") | |||||
| } | |||||
| if strings.HasPrefix(part, "Signature=") { | |||||
| signature = strings.TrimPrefix(part, "Signature=") | |||||
| } | |||||
| } | |||||
| if credential == "" || signedHeaders == "" || signature == "" { | |||||
| return "", nil, "", fmt.Errorf("missing necessary parts in Authorization header") | |||||
| } | |||||
| headers := strings.Split(signedHeaders, ";") | |||||
| return credential, headers, signature, nil | |||||
| } | |||||
| func (a *AWSAuth) getSignature(req *http.Request) string { | |||||
| auth := req.Header.Get(AuthorizationHeader) | |||||
| idx := strings.Index(auth, "Signature=") | |||||
| if idx == -1 { | |||||
| return "" | |||||
| } | |||||
| return auth[idx+len("Signature="):] | |||||
| } | |||||
| @@ -28,10 +28,10 @@ func (s *Server) Object() *ObjectService { | |||||
| } | } | ||||
| } | } | ||||
| func (s *ObjectService) List(ctx *gin.Context) { | |||||
| log := logger.WithField("HTTP", "Object.List") | |||||
| func (s *ObjectService) ListByPath(ctx *gin.Context) { | |||||
| log := logger.WithField("HTTP", "Object.ListByPath") | |||||
| var req cdsapi.ObjectList | |||||
| var req cdsapi.ObjectListByPath | |||||
| if err := ctx.ShouldBindQuery(&req); err != nil { | if err := ctx.ShouldBindQuery(&req); err != nil { | ||||
| log.Warnf("binding body: %s", err.Error()) | log.Warnf("binding body: %s", err.Error()) | ||||
| ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument")) | ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument")) | ||||
| @@ -45,7 +45,27 @@ func (s *ObjectService) List(ctx *gin.Context) { | |||||
| return | return | ||||
| } | } | ||||
| ctx.JSON(http.StatusOK, OK(cdsapi.ObjectListResp{Objects: objs})) | |||||
| ctx.JSON(http.StatusOK, OK(cdsapi.ObjectListByPathResp{Objects: objs})) | |||||
| } | |||||
| func (s *ObjectService) ListByIDs(ctx *gin.Context) { | |||||
| log := logger.WithField("HTTP", "Object.ListByIDs") | |||||
| var req cdsapi.ObjectListByIDs | |||||
| if err := ctx.ShouldBindJSON(&req); err != nil { | |||||
| log.Warnf("binding body: %s", err.Error()) | |||||
| ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument")) | |||||
| return | |||||
| } | |||||
| objs, err := s.svc.ObjectSvc().GetByIDs(req.UserID, req.ObjectIDs) | |||||
| if err != nil { | |||||
| log.Warnf("listing objects: %s", err.Error()) | |||||
| ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("listing objects: %v", err))) | |||||
| return | |||||
| } | |||||
| ctx.JSON(http.StatusOK, OK(cdsapi.ObjectListByIDsResp{Objects: objs})) | |||||
| } | } | ||||
| type ObjectUploadReq struct { | type ObjectUploadReq struct { | ||||
| @@ -63,7 +83,7 @@ func (s *ObjectService) Upload(ctx *gin.Context) { | |||||
| return | return | ||||
| } | } | ||||
| up, err := s.svc.Uploader.BeginUpdate(req.Info.UserID, req.Info.PackageID, req.Info.Affinity) | |||||
| up, err := s.svc.Uploader.BeginUpdate(req.Info.UserID, req.Info.PackageID, req.Info.Affinity, req.Info.LoadTo, req.Info.LoadToPath) | |||||
| if err != nil { | if err != nil { | ||||
| log.Warnf("begin update: %s", err.Error()) | log.Warnf("begin update: %s", err.Error()) | ||||
| ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("begin update: %v", err))) | ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("begin update: %v", err))) | ||||
| @@ -138,6 +158,11 @@ func (s *ObjectService) Download(ctx *gin.Context) { | |||||
| ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "download object failed")) | ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "download object failed")) | ||||
| return | return | ||||
| } | } | ||||
| if file.File == nil { | |||||
| log.Warnf("object not found: %d", req.ObjectID) | |||||
| ctx.JSON(http.StatusOK, Failed(errorcode.DataNotFound, "object not found")) | |||||
| return | |||||
| } | |||||
| defer file.File.Close() | defer file.File.Close() | ||||
| ctx.Header("Content-Disposition", "attachment; filename="+url.PathEscape(path.Base(file.Object.Path))) | ctx.Header("Content-Disposition", "attachment; filename="+url.PathEscape(path.Base(file.Object.Path))) | ||||
| @@ -338,6 +363,26 @@ func (s *ObjectService) DeleteByPath(ctx *gin.Context) { | |||||
| ctx.JSON(http.StatusOK, OK(nil)) | ctx.JSON(http.StatusOK, OK(nil)) | ||||
| } | } | ||||
| func (s *ObjectService) Clone(ctx *gin.Context) { | |||||
| log := logger.WithField("HTTP", "Object.Clone") | |||||
| var req cdsapi.ObjectClone | |||||
| if err := ctx.ShouldBindJSON(&req); err != nil { | |||||
| log.Warnf("binding body: %s", err.Error()) | |||||
| ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument")) | |||||
| return | |||||
| } | |||||
| objs, err := s.svc.ObjectSvc().Clone(req.UserID, req.Clonings) | |||||
| if err != nil { | |||||
| log.Warnf("cloning object: %s", err.Error()) | |||||
| ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "clone object failed")) | |||||
| return | |||||
| } | |||||
| ctx.JSON(http.StatusOK, OK(cdsapi.ObjectCloneResp{Objects: objs})) | |||||
| } | |||||
| func (s *ObjectService) GetPackageObjects(ctx *gin.Context) { | func (s *ObjectService) GetPackageObjects(ctx *gin.Context) { | ||||
| log := logger.WithField("HTTP", "Object.GetPackageObjects") | log := logger.WithField("HTTP", "Object.GetPackageObjects") | ||||
| @@ -46,24 +46,24 @@ func (s *PackageService) Get(ctx *gin.Context) { | |||||
| ctx.JSON(http.StatusOK, OK(cdsapi.PackageGetResp{Package: *pkg})) | ctx.JSON(http.StatusOK, OK(cdsapi.PackageGetResp{Package: *pkg})) | ||||
| } | } | ||||
| func (s *PackageService) GetByName(ctx *gin.Context) { | |||||
| log := logger.WithField("HTTP", "Package.GetByName") | |||||
| func (s *PackageService) GetByFullName(ctx *gin.Context) { | |||||
| log := logger.WithField("HTTP", "Package.GetByFullName") | |||||
| var req cdsapi.PackageGetByName | |||||
| var req cdsapi.PackageGetByFullName | |||||
| if err := ctx.ShouldBindQuery(&req); err != nil { | if err := ctx.ShouldBindQuery(&req); err != nil { | ||||
| log.Warnf("binding query: %s", err.Error()) | log.Warnf("binding query: %s", err.Error()) | ||||
| ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument")) | ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument")) | ||||
| return | return | ||||
| } | } | ||||
| pkg, err := s.svc.PackageSvc().GetByName(req.UserID, req.BucketName, req.PackageName) | |||||
| pkg, err := s.svc.PackageSvc().GetByFullName(req.UserID, req.BucketName, req.PackageName) | |||||
| if err != nil { | if err != nil { | ||||
| log.Warnf("getting package by name: %s", err.Error()) | log.Warnf("getting package by name: %s", err.Error()) | ||||
| ctx.JSON(http.StatusOK, FailedError(err)) | ctx.JSON(http.StatusOK, FailedError(err)) | ||||
| return | return | ||||
| } | } | ||||
| ctx.JSON(http.StatusOK, OK(cdsapi.PackageGetByNameResp{Package: *pkg})) | |||||
| ctx.JSON(http.StatusOK, OK(cdsapi.PackageGetByFullNameResp{Package: *pkg})) | |||||
| } | } | ||||
| // Create 处理创建新包的HTTP请求。 | // Create 处理创建新包的HTTP请求。 | ||||
| @@ -103,7 +103,13 @@ func (s *PackageService) CreateLoad(ctx *gin.Context) { | |||||
| return | return | ||||
| } | } | ||||
| up, err := s.svc.Uploader.BeginCreateLoad(req.Info.UserID, req.Info.BucketID, req.Info.Name, req.Info.LoadTo) | |||||
| if len(req.Info.LoadTo) != len(req.Info.LoadToPath) { | |||||
| log.Warnf("load to and load to path count not match") | |||||
| ctx.JSON(http.StatusOK, Failed(errorcode.BadArgument, "load to and load to path count not match")) | |||||
| return | |||||
| } | |||||
| up, err := s.svc.Uploader.BeginCreateLoad(req.Info.UserID, req.Info.BucketID, req.Info.Name, req.Info.LoadTo, req.Info.LoadToPath) | |||||
| if err != nil { | if err != nil { | ||||
| log.Warnf("begin package create load: %s", err.Error()) | log.Warnf("begin package create load: %s", err.Error()) | ||||
| ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("begin package create load: %v", err))) | ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("begin package create load: %v", err))) | ||||
| @@ -149,7 +155,7 @@ func (s *PackageService) CreateLoad(ctx *gin.Context) { | |||||
| objs[i] = ret.Objects[pathes[i]] | objs[i] = ret.Objects[pathes[i]] | ||||
| } | } | ||||
| ctx.JSON(http.StatusOK, OK(cdsapi.PackageCreateLoadResp{Package: ret.Package, Objects: objs, LoadedDirs: ret.LoadedDirs})) | |||||
| ctx.JSON(http.StatusOK, OK(cdsapi.PackageCreateLoadResp{Package: ret.Package, Objects: objs})) | |||||
| } | } | ||||
| func (s *PackageService) Delete(ctx *gin.Context) { | func (s *PackageService) Delete(ctx *gin.Context) { | ||||
| @@ -172,6 +178,28 @@ func (s *PackageService) Delete(ctx *gin.Context) { | |||||
| ctx.JSON(http.StatusOK, OK(nil)) | ctx.JSON(http.StatusOK, OK(nil)) | ||||
| } | } | ||||
| func (s *PackageService) Clone(ctx *gin.Context) { | |||||
| log := logger.WithField("HTTP", "Package.Clone") | |||||
| var req cdsapi.PackageClone | |||||
| if err := ctx.ShouldBindJSON(&req); err != nil { | |||||
| log.Warnf("binding body: %s", err.Error()) | |||||
| ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument")) | |||||
| return | |||||
| } | |||||
| pkg, err := s.svc.PackageSvc().Clone(req.UserID, req.PackageID, req.BucketID, req.Name) | |||||
| if err != nil { | |||||
| log.Warnf("cloning package: %s", err.Error()) | |||||
| ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "clone package failed")) | |||||
| return | |||||
| } | |||||
| ctx.JSON(http.StatusOK, OK(cdsapi.PackageCloneResp{ | |||||
| Package: pkg, | |||||
| })) | |||||
| } | |||||
| func (s *PackageService) ListBucketPackages(ctx *gin.Context) { | func (s *PackageService) ListBucketPackages(ctx *gin.Context) { | ||||
| log := logger.WithField("HTTP", "Package.ListBucketPackages") | log := logger.WithField("HTTP", "Package.ListBucketPackages") | ||||
| @@ -214,26 +242,3 @@ func (s *PackageService) GetCachedStorages(ctx *gin.Context) { | |||||
| ctx.JSON(http.StatusOK, OK(cdsapi.PackageGetCachedStoragesResp{PackageCachingInfo: resp})) | ctx.JSON(http.StatusOK, OK(cdsapi.PackageGetCachedStoragesResp{PackageCachingInfo: resp})) | ||||
| } | } | ||||
| // GetLoadedStorages 处理获取包的加载节点的HTTP请求。 | |||||
| func (s *PackageService) GetLoadedStorages(ctx *gin.Context) { | |||||
| log := logger.WithField("HTTP", "Package.GetLoadedStorages") | |||||
| var req cdsapi.PackageGetLoadedStoragesReq | |||||
| if err := ctx.ShouldBindQuery(&req); err != nil { | |||||
| log.Warnf("binding query: %s", err.Error()) | |||||
| ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument")) | |||||
| return | |||||
| } | |||||
| stgIDs, err := s.svc.PackageSvc().GetLoadedStorages(req.UserID, req.PackageID) | |||||
| if err != nil { | |||||
| log.Warnf("get package loaded storages failed: %s", err.Error()) | |||||
| ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "get package loaded storages failed")) | |||||
| return | |||||
| } | |||||
| ctx.JSON(http.StatusOK, OK(cdsapi.PackageGetLoadedStoragesResp{ | |||||
| StorageIDs: stgIDs, | |||||
| })) | |||||
| } | |||||
| @@ -11,15 +11,17 @@ type Server struct { | |||||
| engine *gin.Engine | engine *gin.Engine | ||||
| listenAddr string | listenAddr string | ||||
| svc *services.Service | svc *services.Service | ||||
| awsAuth *AWSAuth | |||||
| } | } | ||||
| func NewServer(listenAddr string, svc *services.Service) (*Server, error) { | |||||
| func NewServer(listenAddr string, svc *services.Service, awsAuth *AWSAuth) (*Server, error) { | |||||
| engine := gin.New() | engine := gin.New() | ||||
| return &Server{ | return &Server{ | ||||
| engine: engine, | engine: engine, | ||||
| listenAddr: listenAddr, | listenAddr: listenAddr, | ||||
| svc: svc, | svc: svc, | ||||
| awsAuth: awsAuth, | |||||
| }, nil | }, nil | ||||
| } | } | ||||
| @@ -43,7 +45,10 @@ func (s *Server) initRouters() { | |||||
| // initTemp(rt, s) | // initTemp(rt, s) | ||||
| rt.GET(cdsapi.ObjectListPath, s.Object().List) | |||||
| s.routeV1(s.engine) | |||||
| rt.GET(cdsapi.ObjectListPathByPath, s.Object().ListByPath) | |||||
| rt.POST(cdsapi.ObjectListByIDsPath, s.Object().ListByIDs) | |||||
| rt.GET(cdsapi.ObjectDownloadPath, s.Object().Download) | rt.GET(cdsapi.ObjectDownloadPath, s.Object().Download) | ||||
| rt.GET(cdsapi.ObjectDownloadByPathPath, s.Object().DownloadByPath) | rt.GET(cdsapi.ObjectDownloadByPathPath, s.Object().DownloadByPath) | ||||
| rt.POST(cdsapi.ObjectUploadPath, s.Object().Upload) | rt.POST(cdsapi.ObjectUploadPath, s.Object().Upload) | ||||
| @@ -53,15 +58,16 @@ func (s *Server) initRouters() { | |||||
| rt.POST(cdsapi.ObjectMovePath, s.Object().Move) | rt.POST(cdsapi.ObjectMovePath, s.Object().Move) | ||||
| rt.POST(cdsapi.ObjectDeletePath, s.Object().Delete) | rt.POST(cdsapi.ObjectDeletePath, s.Object().Delete) | ||||
| rt.POST(cdsapi.ObjectDeleteByPathPath, s.Object().DeleteByPath) | rt.POST(cdsapi.ObjectDeleteByPathPath, s.Object().DeleteByPath) | ||||
| rt.POST(cdsapi.ObjectClonePath, s.Object().Clone) | |||||
| rt.GET(cdsapi.PackageGetPath, s.Package().Get) | rt.GET(cdsapi.PackageGetPath, s.Package().Get) | ||||
| rt.GET(cdsapi.PackageGetByNamePath, s.Package().GetByName) | |||||
| rt.GET(cdsapi.PackageGetByFullNamePath, s.Package().GetByFullName) | |||||
| rt.POST(cdsapi.PackageCreatePath, s.Package().Create) | rt.POST(cdsapi.PackageCreatePath, s.Package().Create) | ||||
| rt.POST(cdsapi.PackageCreateLoadPath, s.Package().CreateLoad) | rt.POST(cdsapi.PackageCreateLoadPath, s.Package().CreateLoad) | ||||
| rt.POST(cdsapi.PackageDeletePath, s.Package().Delete) | rt.POST(cdsapi.PackageDeletePath, s.Package().Delete) | ||||
| rt.POST(cdsapi.PackageClonePath, s.Package().Clone) | |||||
| rt.GET(cdsapi.PackageListBucketPackagesPath, s.Package().ListBucketPackages) | rt.GET(cdsapi.PackageListBucketPackagesPath, s.Package().ListBucketPackages) | ||||
| rt.GET(cdsapi.PackageGetCachedStoragesPath, s.Package().GetCachedStorages) | rt.GET(cdsapi.PackageGetCachedStoragesPath, s.Package().GetCachedStorages) | ||||
| rt.GET(cdsapi.PackageGetLoadedStoragesPath, s.Package().GetLoadedStorages) | |||||
| rt.POST(cdsapi.StorageLoadPackagePath, s.Storage().LoadPackage) | rt.POST(cdsapi.StorageLoadPackagePath, s.Storage().LoadPackage) | ||||
| rt.POST(cdsapi.StorageCreatePackagePath, s.Storage().CreatePackage) | rt.POST(cdsapi.StorageCreatePackagePath, s.Storage().CreatePackage) | ||||
| @@ -73,4 +79,42 @@ func (s *Server) initRouters() { | |||||
| rt.POST(cdsapi.BucketCreatePath, s.Bucket().Create) | rt.POST(cdsapi.BucketCreatePath, s.Bucket().Create) | ||||
| rt.POST(cdsapi.BucketDeletePath, s.Bucket().Delete) | rt.POST(cdsapi.BucketDeletePath, s.Bucket().Delete) | ||||
| rt.GET(cdsapi.BucketListUserBucketsPath, s.Bucket().ListUserBuckets) | rt.GET(cdsapi.BucketListUserBucketsPath, s.Bucket().ListUserBuckets) | ||||
| } | |||||
| func (s *Server) routeV1(eg *gin.Engine) { | |||||
| v1 := eg.Group("/v1") | |||||
| v1.GET(cdsapi.ObjectListPathByPath, s.awsAuth.Auth, s.Object().ListByPath) | |||||
| v1.POST(cdsapi.ObjectListByIDsPath, s.awsAuth.Auth, s.Object().ListByIDs) | |||||
| v1.GET(cdsapi.ObjectDownloadPath, s.awsAuth.Auth, s.Object().Download) | |||||
| v1.GET(cdsapi.ObjectDownloadByPathPath, s.awsAuth.Auth, s.Object().DownloadByPath) | |||||
| v1.POST(cdsapi.ObjectUploadPath, s.awsAuth.AuthWithoutBody, s.Object().Upload) | |||||
| v1.GET(cdsapi.ObjectGetPackageObjectsPath, s.awsAuth.Auth, s.Object().GetPackageObjects) | |||||
| v1.POST(cdsapi.ObjectUpdateInfoPath, s.awsAuth.Auth, s.Object().UpdateInfo) | |||||
| v1.POST(cdsapi.ObjectUpdateInfoByPathPath, s.awsAuth.Auth, s.Object().UpdateInfoByPath) | |||||
| v1.POST(cdsapi.ObjectMovePath, s.awsAuth.Auth, s.Object().Move) | |||||
| v1.POST(cdsapi.ObjectDeletePath, s.awsAuth.Auth, s.Object().Delete) | |||||
| v1.POST(cdsapi.ObjectDeleteByPathPath, s.awsAuth.Auth, s.Object().DeleteByPath) | |||||
| v1.POST(cdsapi.ObjectClonePath, s.awsAuth.Auth, s.Object().Clone) | |||||
| v1.GET(cdsapi.PackageGetPath, s.awsAuth.Auth, s.Package().Get) | |||||
| v1.GET(cdsapi.PackageGetByFullNamePath, s.awsAuth.Auth, s.Package().GetByFullName) | |||||
| v1.POST(cdsapi.PackageCreatePath, s.awsAuth.Auth, s.Package().Create) | |||||
| v1.POST(cdsapi.PackageCreateLoadPath, s.awsAuth.Auth, s.Package().CreateLoad) | |||||
| v1.POST(cdsapi.PackageDeletePath, s.awsAuth.Auth, s.Package().Delete) | |||||
| v1.POST(cdsapi.PackageClonePath, s.awsAuth.Auth, s.Package().Clone) | |||||
| v1.GET(cdsapi.PackageListBucketPackagesPath, s.awsAuth.Auth, s.Package().ListBucketPackages) | |||||
| v1.GET(cdsapi.PackageGetCachedStoragesPath, s.awsAuth.Auth, s.Package().GetCachedStorages) | |||||
| v1.POST(cdsapi.StorageLoadPackagePath, s.awsAuth.Auth, s.Storage().LoadPackage) | |||||
| v1.POST(cdsapi.StorageCreatePackagePath, s.awsAuth.Auth, s.Storage().CreatePackage) | |||||
| v1.GET(cdsapi.StorageGetPath, s.awsAuth.Auth, s.Storage().Get) | |||||
| v1.POST(cdsapi.CacheMovePackagePath, s.awsAuth.Auth, s.Cache().MovePackage) | |||||
| v1.GET(cdsapi.BucketGetByNamePath, s.awsAuth.Auth, s.Bucket().GetByName) | |||||
| v1.POST(cdsapi.BucketCreatePath, s.awsAuth.Auth, s.Bucket().Create) | |||||
| v1.POST(cdsapi.BucketDeletePath, s.awsAuth.Auth, s.Bucket().Delete) | |||||
| v1.GET(cdsapi.BucketListUserBucketsPath, s.awsAuth.Auth, s.Bucket().ListUserBuckets) | |||||
| } | } | ||||
| @@ -1,9 +1,7 @@ | |||||
| package http | package http | ||||
| import ( | import ( | ||||
| "fmt" | |||||
| "net/http" | "net/http" | ||||
| "path/filepath" | |||||
| "time" | "time" | ||||
| "github.com/gin-gonic/gin" | "github.com/gin-gonic/gin" | ||||
| @@ -32,37 +30,14 @@ func (s *StorageService) LoadPackage(ctx *gin.Context) { | |||||
| return | return | ||||
| } | } | ||||
| hubID, taskID, err := s.svc.StorageSvc().StartStorageLoadPackage(req.UserID, req.PackageID, req.StorageID) | |||||
| err := s.svc.StorageSvc().LoadPackage(req.UserID, req.PackageID, req.StorageID, req.RootPath) | |||||
| if err != nil { | if err != nil { | ||||
| log.Warnf("start storage load package: %s", err.Error()) | |||||
| ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("start loading: %v", err))) | |||||
| log.Warnf("loading package: %s", err.Error()) | |||||
| ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "loading package failed")) | |||||
| return | return | ||||
| } | } | ||||
| for { | |||||
| complete, ret, err := s.svc.StorageSvc().WaitStorageLoadPackage(hubID, taskID, time.Second*10) | |||||
| if complete { | |||||
| if err != nil { | |||||
| log.Warnf("loading complete with: %s", err.Error()) | |||||
| ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("loading complete with: %v", err))) | |||||
| return | |||||
| } | |||||
| ctx.JSON(http.StatusOK, OK(cdsapi.StorageLoadPackageResp{ | |||||
| FullPath: filepath.Join(ret.RemoteBase, ret.PackagePath), | |||||
| PackagePath: ret.PackagePath, | |||||
| LocalBase: ret.LocalBase, | |||||
| RemoteBase: ret.RemoteBase, | |||||
| })) | |||||
| return | |||||
| } | |||||
| if err != nil { | |||||
| log.Warnf("wait loadding: %s", err.Error()) | |||||
| ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("wait loading: %v", err))) | |||||
| return | |||||
| } | |||||
| } | |||||
| ctx.JSON(http.StatusOK, OK(cdsapi.StorageLoadPackageResp{})) | |||||
| } | } | ||||
| func (s *StorageService) CreatePackage(ctx *gin.Context) { | func (s *StorageService) CreatePackage(ctx *gin.Context) { | ||||
| @@ -9,6 +9,7 @@ import ( | |||||
| stgglb "gitlink.org.cn/cloudream/storage/common/globals" | stgglb "gitlink.org.cn/cloudream/storage/common/globals" | ||||
| agtmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/agent" | agtmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/agent" | ||||
| coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator" | coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/factory" | |||||
| ) | ) | ||||
| type CacheService struct { | type CacheService struct { | ||||
| @@ -31,7 +32,7 @@ func (svc *CacheService) StartCacheMovePackage(userID cdssdk.UserID, packageID c | |||||
| return 0, "", fmt.Errorf("get storage detail: %w", err) | return 0, "", fmt.Errorf("get storage detail: %w", err) | ||||
| } | } | ||||
| if getStg.Storages[0].Storage.ShardStore == nil { | |||||
| if !factory.GetBuilder(*getStg.Storages[0]).ShardStoreDesc().Enabled() { | |||||
| return 0, "", fmt.Errorf("shard storage is not enabled") | return 0, "", fmt.Errorf("shard storage is not enabled") | ||||
| } | } | ||||
| @@ -26,7 +26,7 @@ func (svc *Service) HubSvc() *HubService { | |||||
| // | // | ||||
| // []cdssdk.Hub - 获取到的节点信息列表 | // []cdssdk.Hub - 获取到的节点信息列表 | ||||
| // error - 如果过程中发生错误,则返回错误信息 | // error - 如果过程中发生错误,则返回错误信息 | ||||
| func (svc *HubService) GetHubs(hubIDs []cdssdk.HubID) ([]cdssdk.Hub, error) { | |||||
| func (svc *HubService) GetHubs(hubIDs []cdssdk.HubID) ([]*cdssdk.Hub, error) { | |||||
| // 从协调器MQ池中获取一个客户端实例 | // 从协调器MQ池中获取一个客户端实例 | ||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | coorCli, err := stgglb.CoordinatorMQPool.Acquire() | ||||
| if err != nil { | if err != nil { | ||||
| @@ -37,6 +37,21 @@ func (svc *ObjectService) GetByPath(userID cdssdk.UserID, pkgID cdssdk.PackageID | |||||
| return listResp.Objects, nil | return listResp.Objects, nil | ||||
| } | } | ||||
| func (svc *ObjectService) GetByIDs(userID cdssdk.UserID, objectIDs []cdssdk.ObjectID) ([]*cdssdk.Object, error) { | |||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("new coordinator client: %w", err) | |||||
| } | |||||
| defer stgglb.CoordinatorMQPool.Release(coorCli) | |||||
| listResp, err := coorCli.GetObjects(coormq.ReqGetObjects(userID, objectIDs)) | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("requsting to coodinator: %w", err) | |||||
| } | |||||
| return listResp.Objects, nil | |||||
| } | |||||
| func (svc *ObjectService) UpdateInfo(userID cdssdk.UserID, updatings []cdsapi.UpdatingObject) ([]cdssdk.ObjectID, error) { | func (svc *ObjectService) UpdateInfo(userID cdssdk.UserID, updatings []cdsapi.UpdatingObject) ([]cdssdk.ObjectID, error) { | ||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | coorCli, err := stgglb.CoordinatorMQPool.Acquire() | ||||
| if err != nil { | if err != nil { | ||||
| @@ -98,6 +113,21 @@ func (svc *ObjectService) Delete(userID cdssdk.UserID, objectIDs []cdssdk.Object | |||||
| return nil | return nil | ||||
| } | } | ||||
| func (svc *ObjectService) Clone(userID cdssdk.UserID, clonings []cdsapi.CloningObject) ([]*cdssdk.Object, error) { | |||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("new coordinator client: %w", err) | |||||
| } | |||||
| defer stgglb.CoordinatorMQPool.Release(coorCli) | |||||
| resp, err := coorCli.CloneObjects(coormq.ReqCloneObjects(userID, clonings)) | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("requsting to coodinator: %w", err) | |||||
| } | |||||
| return resp.Objects, nil | |||||
| } | |||||
| // GetPackageObjects 获取包中的对象列表。 | // GetPackageObjects 获取包中的对象列表。 | ||||
| // userID: 用户ID。 | // userID: 用户ID。 | ||||
| // packageID: 包ID。 | // packageID: 包ID。 | ||||
| @@ -36,7 +36,7 @@ func (svc *PackageService) Get(userID cdssdk.UserID, packageID cdssdk.PackageID) | |||||
| return &getResp.Package, nil | return &getResp.Package, nil | ||||
| } | } | ||||
| func (svc *PackageService) GetByName(userID cdssdk.UserID, bucketName string, packageName string) (*cdssdk.Package, error) { | |||||
| func (svc *PackageService) GetByFullName(userID cdssdk.UserID, bucketName string, packageName string) (*cdssdk.Package, error) { | |||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | coorCli, err := stgglb.CoordinatorMQPool.Acquire() | ||||
| if err != nil { | if err != nil { | ||||
| return nil, fmt.Errorf("new coordinator client: %w", err) | return nil, fmt.Errorf("new coordinator client: %w", err) | ||||
| @@ -106,6 +106,21 @@ func (svc *PackageService) DeletePackage(userID cdssdk.UserID, packageID cdssdk. | |||||
| return nil | return nil | ||||
| } | } | ||||
| func (svc *PackageService) Clone(userID cdssdk.UserID, packageID cdssdk.PackageID, bucketID cdssdk.BucketID, name string) (cdssdk.Package, error) { | |||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | |||||
| if err != nil { | |||||
| return cdssdk.Package{}, fmt.Errorf("new coordinator client: %w", err) | |||||
| } | |||||
| defer stgglb.CoordinatorMQPool.Release(coorCli) | |||||
| resp, err := coorCli.ClonePackage(coormq.ReqClonePackage(userID, packageID, bucketID, name)) | |||||
| if err != nil { | |||||
| return cdssdk.Package{}, fmt.Errorf("cloning package: %w", err) | |||||
| } | |||||
| return resp.Package, nil | |||||
| } | |||||
| // GetCachedStorages 获取指定包的缓存节点信息 | // GetCachedStorages 获取指定包的缓存节点信息 | ||||
| func (svc *PackageService) GetCachedStorages(userID cdssdk.UserID, packageID cdssdk.PackageID) (cdssdk.PackageCachingInfo, error) { | func (svc *PackageService) GetCachedStorages(userID cdssdk.UserID, packageID cdssdk.PackageID) (cdssdk.PackageCachingInfo, error) { | ||||
| // 从协调器MQ池中获取客户端 | // 从协调器MQ池中获取客户端 | ||||
| @@ -128,20 +143,3 @@ func (svc *PackageService) GetCachedStorages(userID cdssdk.UserID, packageID cds | |||||
| } | } | ||||
| return tmp, nil | return tmp, nil | ||||
| } | } | ||||
| // GetLoadedStorages 获取指定包加载的节点列表 | |||||
| func (svc *PackageService) GetLoadedStorages(userID cdssdk.UserID, packageID cdssdk.PackageID) ([]cdssdk.StorageID, error) { | |||||
| // 从协调器MQ池中获取客户端 | |||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("new coordinator client: %w", err) | |||||
| } | |||||
| defer stgglb.CoordinatorMQPool.Release(coorCli) | |||||
| // 向协调器请求获取加载指定包的节点ID列表 | |||||
| resp, err := coorCli.GetPackageLoadedStorages(coormq.ReqGetPackageLoadedStorages(userID, packageID)) | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("get package loaded storages: %w", err) | |||||
| } | |||||
| return resp.StorageIDs, nil | |||||
| } | |||||
| @@ -7,24 +7,38 @@ import ( | |||||
| "gitlink.org.cn/cloudream/storage/client/internal/task" | "gitlink.org.cn/cloudream/storage/client/internal/task" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/accessstat" | "gitlink.org.cn/cloudream/storage/common/pkgs/accessstat" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/downloader" | "gitlink.org.cn/cloudream/storage/common/pkgs/downloader" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/downloader/strategy" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/metacache" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/uploader" | "gitlink.org.cn/cloudream/storage/common/pkgs/uploader" | ||||
| ) | ) | ||||
| // Service 结构体封装了分布锁服务和任务管理服务。 | // Service 结构体封装了分布锁服务和任务管理服务。 | ||||
| type Service struct { | type Service struct { | ||||
| DistLock *distlock.Service | |||||
| TaskMgr *task.Manager | |||||
| Downloader *downloader.Downloader | |||||
| AccessStat *accessstat.AccessStat | |||||
| Uploader *uploader.Uploader | |||||
| DistLock *distlock.Service | |||||
| TaskMgr *task.Manager | |||||
| Downloader *downloader.Downloader | |||||
| AccessStat *accessstat.AccessStat | |||||
| Uploader *uploader.Uploader | |||||
| StrategySelector *strategy.Selector | |||||
| StorageMeta *metacache.StorageMeta | |||||
| } | } | ||||
| func NewService(distlock *distlock.Service, taskMgr *task.Manager, downloader *downloader.Downloader, accStat *accessstat.AccessStat, uploder *uploader.Uploader) (*Service, error) { | |||||
| func NewService( | |||||
| distlock *distlock.Service, | |||||
| taskMgr *task.Manager, | |||||
| downloader *downloader.Downloader, | |||||
| accStat *accessstat.AccessStat, | |||||
| uploder *uploader.Uploader, | |||||
| strategySelector *strategy.Selector, | |||||
| storageMeta *metacache.StorageMeta, | |||||
| ) (*Service, error) { | |||||
| return &Service{ | return &Service{ | ||||
| DistLock: distlock, | |||||
| TaskMgr: taskMgr, | |||||
| Downloader: downloader, | |||||
| AccessStat: accStat, | |||||
| Uploader: uploder, | |||||
| DistLock: distlock, | |||||
| TaskMgr: taskMgr, | |||||
| Downloader: downloader, | |||||
| AccessStat: accStat, | |||||
| Uploader: uploder, | |||||
| StrategySelector: strategySelector, | |||||
| StorageMeta: storageMeta, | |||||
| }, nil | }, nil | ||||
| } | } | ||||
| @@ -1,15 +1,23 @@ | |||||
| package services | package services | ||||
| import ( | import ( | ||||
| "context" | |||||
| "fmt" | "fmt" | ||||
| "path" | |||||
| "time" | "time" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | |||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | ||||
| stgglb "gitlink.org.cn/cloudream/storage/common/globals" | stgglb "gitlink.org.cn/cloudream/storage/common/globals" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/db2/model" | "gitlink.org.cn/cloudream/storage/common/pkgs/db2/model" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/distlock/reqbuilder" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/downloader/strategy" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/parser" | |||||
| agtmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/agent" | agtmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/agent" | ||||
| coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator" | coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/factory" | |||||
| ) | ) | ||||
| type StorageService struct { | type StorageService struct { | ||||
| @@ -50,74 +58,90 @@ func (svc *StorageService) GetByName(userID cdssdk.UserID, name string) (*model. | |||||
| return &getResp.Storage, nil | return &getResp.Storage, nil | ||||
| } | } | ||||
| func (svc *StorageService) StartStorageLoadPackage(userID cdssdk.UserID, packageID cdssdk.PackageID, storageID cdssdk.StorageID) (cdssdk.HubID, string, error) { | |||||
| func (svc *StorageService) LoadPackage(userID cdssdk.UserID, packageID cdssdk.PackageID, storageID cdssdk.StorageID, rootPath string) error { | |||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | coorCli, err := stgglb.CoordinatorMQPool.Acquire() | ||||
| if err != nil { | if err != nil { | ||||
| return 0, "", fmt.Errorf("new coordinator client: %w", err) | |||||
| return fmt.Errorf("new coordinator client: %w", err) | |||||
| } | } | ||||
| defer stgglb.CoordinatorMQPool.Release(coorCli) | defer stgglb.CoordinatorMQPool.Release(coorCli) | ||||
| stgResp, err := coorCli.GetStorageDetails(coormq.ReqGetStorageDetails([]cdssdk.StorageID{storageID})) | |||||
| if err != nil { | |||||
| return 0, "", fmt.Errorf("getting storage info: %w", err) | |||||
| destStg := svc.StorageMeta.Get(storageID) | |||||
| if destStg == nil { | |||||
| return fmt.Errorf("storage not found: %d", storageID) | |||||
| } | } | ||||
| if stgResp.Storages[0].Storage.ShardStore == nil { | |||||
| return 0, "", fmt.Errorf("shard storage is not enabled") | |||||
| if destStg.MasterHub == nil { | |||||
| return fmt.Errorf("storage %v has no master hub", storageID) | |||||
| } | } | ||||
| agentCli, err := stgglb.AgentMQPool.Acquire(stgResp.Storages[0].MasterHub.HubID) | |||||
| details, err := coorCli.GetPackageObjectDetails(coormq.ReqGetPackageObjectDetails(packageID)) | |||||
| if err != nil { | if err != nil { | ||||
| return 0, "", fmt.Errorf("new agent client: %w", err) | |||||
| return err | |||||
| } | } | ||||
| defer stgglb.AgentMQPool.Release(agentCli) | |||||
| startResp, err := agentCli.StartStorageLoadPackage(agtmq.NewStartStorageLoadPackage(userID, packageID, storageID)) | |||||
| if err != nil { | |||||
| return 0, "", fmt.Errorf("start storage load package: %w", err) | |||||
| } | |||||
| var pinned []cdssdk.ObjectID | |||||
| plans := exec.NewPlanBuilder() | |||||
| for _, obj := range details.Objects { | |||||
| strg, err := svc.StrategySelector.Select(strategy.Request{ | |||||
| Detail: obj, | |||||
| DestHub: destStg.MasterHub.HubID, | |||||
| }) | |||||
| if err != nil { | |||||
| return fmt.Errorf("select download strategy: %w", err) | |||||
| } | |||||
| return stgResp.Storages[0].MasterHub.HubID, startResp.TaskID, nil | |||||
| } | |||||
| ft := ioswitch2.NewFromTo() | |||||
| switch strg := strg.(type) { | |||||
| case *strategy.DirectStrategy: | |||||
| ft.AddFrom(ioswitch2.NewFromShardstore(strg.Detail.Object.FileHash, *strg.Storage.MasterHub, strg.Storage, ioswitch2.RawStream())) | |||||
| type StorageLoadPackageResult struct { | |||||
| PackagePath string | |||||
| LocalBase string | |||||
| RemoteBase string | |||||
| } | |||||
| case *strategy.ECReconstructStrategy: | |||||
| for i, b := range strg.Blocks { | |||||
| ft.AddFrom(ioswitch2.NewFromShardstore(b.FileHash, *strg.Storages[i].MasterHub, strg.Storages[i], ioswitch2.ECStream(b.Index))) | |||||
| ft.ECParam = &strg.Redundancy | |||||
| } | |||||
| default: | |||||
| return fmt.Errorf("unsupported download strategy: %T", strg) | |||||
| } | |||||
| func (svc *StorageService) WaitStorageLoadPackage(hubID cdssdk.HubID, taskID string, waitTimeout time.Duration) (bool, *StorageLoadPackageResult, error) { | |||||
| agentCli, err := stgglb.AgentMQPool.Acquire(hubID) | |||||
| if err != nil { | |||||
| // TODO 失败是否要当做任务已经结束? | |||||
| return true, nil, fmt.Errorf("new agent client: %w", err) | |||||
| ft.AddTo(ioswitch2.NewLoadToShared(*destStg.MasterHub, *destStg, path.Join(rootPath, obj.Object.Path))) | |||||
| // 顺便保存到同存储服务的分片存储中 | |||||
| if factory.GetBuilder(*destStg).ShardStoreDesc().Enabled() { | |||||
| ft.AddTo(ioswitch2.NewToShardStore(*destStg.MasterHub, *destStg, ioswitch2.RawStream(), "")) | |||||
| pinned = append(pinned, obj.Object.ObjectID) | |||||
| } | |||||
| err = parser.Parse(ft, plans) | |||||
| if err != nil { | |||||
| return fmt.Errorf("parse plan: %w", err) | |||||
| } | |||||
| } | } | ||||
| defer stgglb.AgentMQPool.Release(agentCli) | |||||
| waitResp, err := agentCli.WaitStorageLoadPackage(agtmq.NewWaitStorageLoadPackage(taskID, waitTimeout.Milliseconds())) | |||||
| mutex, err := reqbuilder.NewBuilder(). | |||||
| // 保护在storage目录中下载的文件 | |||||
| Storage().Buzy(storageID). | |||||
| // 保护下载文件时同时保存到IPFS的文件 | |||||
| Shard().Buzy(storageID). | |||||
| MutexLock(svc.DistLock) | |||||
| if err != nil { | if err != nil { | ||||
| // TODO 请求失败是否要当做任务已经结束? | |||||
| return true, nil, fmt.Errorf("wait storage load package: %w", err) | |||||
| return fmt.Errorf("acquire locks failed, err: %w", err) | |||||
| } | } | ||||
| if !waitResp.IsComplete { | |||||
| return false, nil, nil | |||||
| // 记录访问统计 | |||||
| for _, obj := range details.Objects { | |||||
| svc.AccessStat.AddAccessCounter(obj.Object.ObjectID, packageID, storageID, 1) | |||||
| } | } | ||||
| if waitResp.Error != "" { | |||||
| return true, nil, fmt.Errorf("%s", waitResp.Error) | |||||
| } | |||||
| defer mutex.Unlock() | |||||
| return true, &StorageLoadPackageResult{ | |||||
| PackagePath: waitResp.PackagePath, | |||||
| LocalBase: waitResp.LocalBase, | |||||
| RemoteBase: waitResp.RemoteBase, | |||||
| }, nil | |||||
| } | |||||
| drv := plans.Execute(exec.NewExecContext()) | |||||
| _, err = drv.Wait(context.Background()) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| func (svc *StorageService) DeleteStoragePackage(userID int64, packageID int64, storageID int64) error { | |||||
| // TODO | |||||
| panic("not implement yet") | |||||
| // 失败也没关系 | |||||
| coorCli.StoragePackageLoaded(coormq.ReqStoragePackageLoaded(userID, storageID, packageID, rootPath, pinned)) | |||||
| return nil | |||||
| } | } | ||||
| // 请求节点启动从Storage中上传文件的任务。会返回节点ID和任务ID | // 请求节点启动从Storage中上传文件的任务。会返回节点ID和任务ID | ||||
| @@ -4,14 +4,14 @@ import ( | |||||
| "gitlink.org.cn/cloudream/common/pkgs/distlock" // 引入分布式锁服务 | "gitlink.org.cn/cloudream/common/pkgs/distlock" // 引入分布式锁服务 | ||||
| "gitlink.org.cn/cloudream/common/pkgs/task" // 引入任务处理相关的包 | "gitlink.org.cn/cloudream/common/pkgs/task" // 引入任务处理相关的包 | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/connectivity" // 引入网络连接状态收集器 | "gitlink.org.cn/cloudream/storage/common/pkgs/connectivity" // 引入网络连接状态收集器 | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/svcmgr" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/agtpool" | |||||
| ) | ) | ||||
| // TaskContext 定义了任务执行的上下文环境,包含分布式锁服务和网络连接状态收集器 | // TaskContext 定义了任务执行的上下文环境,包含分布式锁服务和网络连接状态收集器 | ||||
| type TaskContext struct { | type TaskContext struct { | ||||
| distlock *distlock.Service | distlock *distlock.Service | ||||
| connectivity *connectivity.Collector | connectivity *connectivity.Collector | ||||
| stgMgr *svcmgr.Manager | |||||
| stgAgts *agtpool.AgentPool | |||||
| } | } | ||||
| // CompleteFn 类型定义了任务完成时的回调函数,用于设置任务的执行结果 | // CompleteFn 类型定义了任务完成时的回调函数,用于设置任务的执行结果 | ||||
| @@ -31,10 +31,10 @@ type CompleteOption = task.CompleteOption | |||||
| // NewManager 创建一个新的任务管理器实例,接受一个分布式锁服务和一个网络连接状态收集器作为参数 | // NewManager 创建一个新的任务管理器实例,接受一个分布式锁服务和一个网络连接状态收集器作为参数 | ||||
| // 返回一个初始化好的任务管理器实例 | // 返回一个初始化好的任务管理器实例 | ||||
| func NewManager(distlock *distlock.Service, connectivity *connectivity.Collector, stgMgr *svcmgr.Manager) Manager { | |||||
| func NewManager(distlock *distlock.Service, connectivity *connectivity.Collector, stgAgts *agtpool.AgentPool) Manager { | |||||
| return task.NewManager(TaskContext{ | return task.NewManager(TaskContext{ | ||||
| distlock: distlock, | distlock: distlock, | ||||
| connectivity: connectivity, | connectivity: connectivity, | ||||
| stgMgr: stgMgr, | |||||
| stgAgts: stgAgts, | |||||
| }) | }) | ||||
| } | } | ||||
| @@ -18,8 +18,10 @@ import ( | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/connectivity" | "gitlink.org.cn/cloudream/storage/common/pkgs/connectivity" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/distlock" | "gitlink.org.cn/cloudream/storage/common/pkgs/distlock" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/downloader" | "gitlink.org.cn/cloudream/storage/common/pkgs/downloader" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/downloader/strategy" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/metacache" | |||||
| coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator" | coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/svcmgr" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/agtpool" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/uploader" | "gitlink.org.cn/cloudream/storage/common/pkgs/uploader" | ||||
| ) | ) | ||||
| @@ -37,7 +39,7 @@ func main() { | |||||
| } | } | ||||
| stgglb.InitLocal(&config.Cfg().Local) | stgglb.InitLocal(&config.Cfg().Local) | ||||
| stgglb.InitMQPool(&config.Cfg().RabbitMQ) | |||||
| stgglb.InitMQPool(config.Cfg().RabbitMQ) | |||||
| stgglb.InitAgentRPCPool(&config.Cfg().AgentGRPC) | stgglb.InitAgentRPCPool(&config.Cfg().AgentGRPC) | ||||
| // 连接性信息收集 | // 连接性信息收集 | ||||
| @@ -57,13 +59,13 @@ func main() { | |||||
| consMap := make(map[cdssdk.HubID]connectivity.Connectivity) | consMap := make(map[cdssdk.HubID]connectivity.Connectivity) | ||||
| for _, con := range getCons.Connectivities { | for _, con := range getCons.Connectivities { | ||||
| var delay *time.Duration | var delay *time.Duration | ||||
| if con.Delay != nil { | |||||
| d := time.Duration(*con.Delay * float32(time.Millisecond)) | |||||
| if con.Latency != nil { | |||||
| d := time.Duration(*con.Latency * float32(time.Millisecond)) | |||||
| delay = &d | delay = &d | ||||
| } | } | ||||
| consMap[con.FromHubID] = connectivity.Connectivity{ | consMap[con.FromHubID] = connectivity.Connectivity{ | ||||
| ToHubID: con.ToHubID, | ToHubID: con.ToHubID, | ||||
| Delay: delay, | |||||
| Latency: delay, | |||||
| } | } | ||||
| } | } | ||||
| conCol = connectivity.NewCollectorWithInitData(&config.Cfg().Connectivity, nil, consMap) | conCol = connectivity.NewCollectorWithInitData(&config.Cfg().Connectivity, nil, consMap) | ||||
| @@ -75,6 +77,12 @@ func main() { | |||||
| conCol.CollectInPlace() | conCol.CollectInPlace() | ||||
| } | } | ||||
| metaCacheHost := metacache.NewHost() | |||||
| go metaCacheHost.Serve() | |||||
| stgMeta := metaCacheHost.AddStorageMeta() | |||||
| hubMeta := metaCacheHost.AddHubMeta() | |||||
| conMeta := metaCacheHost.AddConnectivity() | |||||
| // 分布式锁 | // 分布式锁 | ||||
| distlockSvc, err := distlock.NewService(&config.Cfg().DistLock) | distlockSvc, err := distlock.NewService(&config.Cfg().DistLock) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -91,18 +99,20 @@ func main() { | |||||
| go serveAccessStat(acStat) | go serveAccessStat(acStat) | ||||
| // 存储管理器 | // 存储管理器 | ||||
| stgMgr := svcmgr.NewManager() | |||||
| stgAgts := agtpool.NewPool() | |||||
| // 任务管理器 | // 任务管理器 | ||||
| taskMgr := task.NewManager(distlockSvc, &conCol, stgMgr) | |||||
| taskMgr := task.NewManager(distlockSvc, &conCol, stgAgts) | |||||
| strgSel := strategy.NewSelector(config.Cfg().DownloadStrategy, stgMeta, hubMeta, conMeta) | |||||
| // 下载器 | // 下载器 | ||||
| dlder := downloader.NewDownloader(config.Cfg().Downloader, &conCol, stgMgr) | |||||
| dlder := downloader.NewDownloader(config.Cfg().Downloader, &conCol, stgAgts, strgSel) | |||||
| // 上传器 | // 上传器 | ||||
| uploader := uploader.NewUploader(distlockSvc, &conCol, stgMgr) | |||||
| uploader := uploader.NewUploader(distlockSvc, &conCol, stgAgts, stgMeta) | |||||
| svc, err := services.NewService(distlockSvc, &taskMgr, &dlder, acStat, uploader) | |||||
| svc, err := services.NewService(distlockSvc, &taskMgr, &dlder, acStat, uploader, strgSel, stgMeta) | |||||
| if err != nil { | if err != nil { | ||||
| logger.Warnf("new services failed, err: %s", err.Error()) | logger.Warnf("new services failed, err: %s", err.Error()) | ||||
| os.Exit(1) | os.Exit(1) | ||||
| @@ -39,7 +39,9 @@ | |||||
| }, | }, | ||||
| "downloader": { | "downloader": { | ||||
| "maxStripCacheCount": 100, | "maxStripCacheCount": 100, | ||||
| "highLatencyHub": 35, | |||||
| "ecStripPrefetchCount": 1 | "ecStripPrefetchCount": 1 | ||||
| }, | |||||
| "downloadStrategy": { | |||||
| "highLatencyHub": 35 | |||||
| } | } | ||||
| } | } | ||||
| @@ -34,8 +34,13 @@ | |||||
| }, | }, | ||||
| "downloader": { | "downloader": { | ||||
| "maxStripCacheCount": 100, | "maxStripCacheCount": 100, | ||||
| "highLatencyHub": 35, | |||||
| "ecStripPrefetchCount": 1 | "ecStripPrefetchCount": 1 | ||||
| }, | }, | ||||
| "storageID": 0 | |||||
| "downloadStrategy": { | |||||
| "highLatencyHub": 35 | |||||
| }, | |||||
| "storageID": 0, | |||||
| "authAccessKey": "", | |||||
| "authSecretKey": "", | |||||
| "maxHttpBodySize": 5242880 | |||||
| } | } | ||||
| @@ -9,15 +9,15 @@ | |||||
| "level": "debug" | "level": "debug" | ||||
| }, | }, | ||||
| "db": { | "db": { | ||||
| "address": "106.75.6.194:3306", | |||||
| "account": "root", | |||||
| "password": "cloudream123456", | |||||
| "address": "127.0.0.1:3306", | |||||
| "account": "", | |||||
| "password": "", | |||||
| "databaseName": "cloudream" | "databaseName": "cloudream" | ||||
| }, | }, | ||||
| "rabbitMQ": { | "rabbitMQ": { | ||||
| "address": "106.75.6.194:5672", | |||||
| "account": "cloudream", | |||||
| "password": "cloudream123456", | |||||
| "address": "127.0.0.1:5672", | |||||
| "account": "", | |||||
| "password": "", | |||||
| "vhost": "/", | "vhost": "/", | ||||
| "param": { | "param": { | ||||
| "retryNum": 5, | "retryNum": 5, | ||||
| @@ -25,7 +25,7 @@ | |||||
| } | } | ||||
| }, | }, | ||||
| "distlock": { | "distlock": { | ||||
| "etcdAddress": "106.75.6.194:2379", | |||||
| "etcdAddress": "127.0.0.1:2379", | |||||
| "etcdUsername": "", | "etcdUsername": "", | ||||
| "etcdPassword": "", | "etcdPassword": "", | ||||
| "etcdLockLeaseTimeSec": 5, | "etcdLockLeaseTimeSec": 5, | ||||
| @@ -1,8 +1,8 @@ | |||||
| package stgglb | package stgglb | ||||
| import ( | import ( | ||||
| "gitlink.org.cn/cloudream/common/pkgs/mq" | |||||
| agtrpc "gitlink.org.cn/cloudream/storage/common/pkgs/grpc/agent" | agtrpc "gitlink.org.cn/cloudream/storage/common/pkgs/grpc/agent" | ||||
| stgmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq" | |||||
| agtmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/agent" | agtmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/agent" | ||||
| coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator" | coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator" | ||||
| scmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/scanner" | scmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/scanner" | ||||
| @@ -18,7 +18,7 @@ var ScannerMQPool scmq.Pool | |||||
| // | // | ||||
| // @Description: 初始化MQ连接池 | // @Description: 初始化MQ连接池 | ||||
| // @param cfg | // @param cfg | ||||
| func InitMQPool(cfg *stgmq.Config) { | |||||
| func InitMQPool(cfg mq.Config) { | |||||
| AgentMQPool = agtmq.NewPool(cfg) | AgentMQPool = agtmq.NewPool(cfg) | ||||
| CoordinatorMQPool = coormq.NewPool(cfg) | CoordinatorMQPool = coormq.NewPool(cfg) | ||||
| @@ -13,7 +13,7 @@ import ( | |||||
| type Connectivity struct { | type Connectivity struct { | ||||
| ToHubID cdssdk.HubID | ToHubID cdssdk.HubID | ||||
| Delay *time.Duration | |||||
| Latency *time.Duration | |||||
| TestTime time.Time | TestTime time.Time | ||||
| } | } | ||||
| @@ -52,17 +52,6 @@ func NewCollectorWithInitData(cfg *Config, onCollected func(collector *Collector | |||||
| return rpt | return rpt | ||||
| } | } | ||||
| func (r *Collector) Get(hubID cdssdk.HubID) *Connectivity { | |||||
| r.lock.RLock() | |||||
| defer r.lock.RUnlock() | |||||
| con, ok := r.connectivities[hubID] | |||||
| if ok { | |||||
| return &con | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func (r *Collector) GetAll() map[cdssdk.HubID]Connectivity { | func (r *Collector) GetAll() map[cdssdk.HubID]Connectivity { | ||||
| r.lock.RLock() | r.lock.RLock() | ||||
| defer r.lock.RUnlock() | defer r.lock.RUnlock() | ||||
| @@ -101,8 +90,8 @@ func (r *Collector) serve() { | |||||
| // 为了防止同时启动的节点会集中进行Ping,所以第一次上报间隔为0-TestInterval秒之间随机 | // 为了防止同时启动的节点会集中进行Ping,所以第一次上报间隔为0-TestInterval秒之间随机 | ||||
| startup := true | startup := true | ||||
| firstReportDelay := time.Duration(float64(r.cfg.TestInterval) * float64(time.Second) * rand.Float64()) | |||||
| ticker := time.NewTicker(firstReportDelay) | |||||
| firstReportLatency := time.Duration(float64(r.cfg.TestInterval) * float64(time.Second) * rand.Float64()) | |||||
| ticker := time.NewTicker(firstReportLatency) | |||||
| loop: | loop: | ||||
| for { | for { | ||||
| @@ -150,7 +139,7 @@ func (r *Collector) testing() { | |||||
| wg.Add(1) | wg.Add(1) | ||||
| go func() { | go func() { | ||||
| defer wg.Done() | defer wg.Done() | ||||
| cons[tmpIdx] = r.ping(tmpHub) | |||||
| cons[tmpIdx] = r.ping(*tmpHub) | |||||
| }() | }() | ||||
| } | } | ||||
| @@ -190,7 +179,7 @@ func (r *Collector) ping(hub cdssdk.Hub) Connectivity { | |||||
| return Connectivity{ | return Connectivity{ | ||||
| ToHubID: hub.HubID, | ToHubID: hub.HubID, | ||||
| Delay: nil, | |||||
| Latency: nil, | |||||
| TestTime: time.Now(), | TestTime: time.Now(), | ||||
| } | } | ||||
| } | } | ||||
| @@ -200,7 +189,7 @@ func (r *Collector) ping(hub cdssdk.Hub) Connectivity { | |||||
| log.Warnf("new agent %v:%v rpc client: %w", ip, port, err) | log.Warnf("new agent %v:%v rpc client: %w", ip, port, err) | ||||
| return Connectivity{ | return Connectivity{ | ||||
| ToHubID: hub.HubID, | ToHubID: hub.HubID, | ||||
| Delay: nil, | |||||
| Latency: nil, | |||||
| TestTime: time.Now(), | TestTime: time.Now(), | ||||
| } | } | ||||
| } | } | ||||
| @@ -212,13 +201,13 @@ func (r *Collector) ping(hub cdssdk.Hub) Connectivity { | |||||
| log.Warnf("pre ping: %v", err) | log.Warnf("pre ping: %v", err) | ||||
| return Connectivity{ | return Connectivity{ | ||||
| ToHubID: hub.HubID, | ToHubID: hub.HubID, | ||||
| Delay: nil, | |||||
| Latency: nil, | |||||
| TestTime: time.Now(), | TestTime: time.Now(), | ||||
| } | } | ||||
| } | } | ||||
| // 后几次ping计算延迟 | // 后几次ping计算延迟 | ||||
| var avgDelay time.Duration | |||||
| var avgLatency time.Duration | |||||
| for i := 0; i < 3; i++ { | for i := 0; i < 3; i++ { | ||||
| start := time.Now() | start := time.Now() | ||||
| err = agtCli.Ping() | err = agtCli.Ping() | ||||
| @@ -226,22 +215,22 @@ func (r *Collector) ping(hub cdssdk.Hub) Connectivity { | |||||
| log.Warnf("ping: %v", err) | log.Warnf("ping: %v", err) | ||||
| return Connectivity{ | return Connectivity{ | ||||
| ToHubID: hub.HubID, | ToHubID: hub.HubID, | ||||
| Delay: nil, | |||||
| Latency: nil, | |||||
| TestTime: time.Now(), | TestTime: time.Now(), | ||||
| } | } | ||||
| } | } | ||||
| delay := time.Since(start) | |||||
| avgDelay += delay | |||||
| latency := time.Since(start) | |||||
| avgLatency += latency | |||||
| // 每次ping之间间隔1秒 | // 每次ping之间间隔1秒 | ||||
| <-time.After(time.Second) | <-time.After(time.Second) | ||||
| } | } | ||||
| delay := avgDelay / 3 | |||||
| latency := avgLatency / 3 | |||||
| return Connectivity{ | return Connectivity{ | ||||
| ToHubID: hub.HubID, | ToHubID: hub.HubID, | ||||
| Delay: &delay, | |||||
| Latency: &latency, | |||||
| TestTime: time.Now(), | TestTime: time.Now(), | ||||
| } | } | ||||
| } | } | ||||
| @@ -113,26 +113,5 @@ func (db *BucketDB) Create(ctx SQLContext, userID cdssdk.UserID, bucketName stri | |||||
| } | } | ||||
| func (db *BucketDB) Delete(ctx SQLContext, bucketID cdssdk.BucketID) error { | func (db *BucketDB) Delete(ctx SQLContext, bucketID cdssdk.BucketID) error { | ||||
| if err := ctx.Exec("DELETE FROM UserBucket WHERE BucketID = ?", bucketID).Error; err != nil { | |||||
| return fmt.Errorf("delete user bucket failed, err: %w", err) | |||||
| } | |||||
| if err := ctx.Exec("DELETE FROM Bucket WHERE BucketID = ?", bucketID).Error; err != nil { | |||||
| return fmt.Errorf("delete bucket failed, err: %w", err) | |||||
| } | |||||
| var pkgIDs []cdssdk.PackageID | |||||
| if err := ctx.Table("Package").Select("PackageID").Where("BucketID = ?", bucketID).Find(&pkgIDs).Error; err != nil { | |||||
| return fmt.Errorf("query package failed, err: %w", err) | |||||
| } | |||||
| for _, pkgID := range pkgIDs { | |||||
| if err := db.Package().SoftDelete(ctx, pkgID); err != nil { | |||||
| return fmt.Errorf("set package selected failed, err: %w", err) | |||||
| } | |||||
| // 失败也没关系,会有定时任务再次尝试 | |||||
| db.Package().DeleteUnused(ctx, pkgID) | |||||
| } | |||||
| return nil | |||||
| return ctx.Delete(&cdssdk.Bucket{}, "BucketID = ?", bucketID).Error | |||||
| } | } | ||||
| @@ -67,24 +67,6 @@ func (Cache) TableName() string { | |||||
| return "Cache" | return "Cache" | ||||
| } | } | ||||
| const ( | |||||
| StoragePackageStateNormal = "Normal" | |||||
| StoragePackageStateDeleted = "Deleted" | |||||
| StoragePackageStateOutdated = "Outdated" | |||||
| ) | |||||
| // Storage当前加载的Package | |||||
| type StoragePackage struct { | |||||
| StorageID cdssdk.StorageID `gorm:"column:StorageID; primaryKey; type:bigint" json:"storageID"` | |||||
| PackageID cdssdk.PackageID `gorm:"column:PackageID; primaryKey; type:bigint" json:"packageID"` | |||||
| UserID cdssdk.UserID `gorm:"column:UserID; primaryKey; type:bigint" json:"userID"` | |||||
| State string `gorm:"column:State; type:varchar(255); not null" json:"state"` | |||||
| } | |||||
| func (StoragePackage) TableName() string { | |||||
| return "StoragePackage" | |||||
| } | |||||
| type Location struct { | type Location struct { | ||||
| LocationID cdssdk.LocationID `gorm:"column:LocationID; primaryKey; type:bigint; autoIncrement" json:"locationID"` | LocationID cdssdk.LocationID `gorm:"column:LocationID; primaryKey; type:bigint; autoIncrement" json:"locationID"` | ||||
| Name string `gorm:"column:Name; type:varchar(255); not null" json:"name"` | Name string `gorm:"column:Name; type:varchar(255); not null" json:"name"` | ||||
| @@ -20,7 +20,7 @@ func (db *DB) Object() *ObjectDB { | |||||
| return &ObjectDB{DB: db} | return &ObjectDB{DB: db} | ||||
| } | } | ||||
| func (db *ObjectDB) GetByID(ctx SQLContext, objectID cdssdk.ObjectID) (model.Object, error) { | |||||
| func (db *ObjectDB) GetByID(ctx SQLContext, objectID cdssdk.ObjectID) (cdssdk.Object, error) { | |||||
| var ret cdssdk.Object | var ret cdssdk.Object | ||||
| err := ctx.Table("Object").Where("ObjectID = ?", objectID).First(&ret).Error | err := ctx.Table("Object").Where("ObjectID = ?", objectID).First(&ret).Error | ||||
| return ret, err | return ret, err | ||||
| @@ -57,7 +57,7 @@ func (db *ObjectDB) BatchTestObjectID(ctx SQLContext, objectIDs []cdssdk.ObjectI | |||||
| return avaiIDMap, nil | return avaiIDMap, nil | ||||
| } | } | ||||
| func (db *ObjectDB) BatchGet(ctx SQLContext, objectIDs []cdssdk.ObjectID) ([]model.Object, error) { | |||||
| func (db *ObjectDB) BatchGet(ctx SQLContext, objectIDs []cdssdk.ObjectID) ([]cdssdk.Object, error) { | |||||
| if len(objectIDs) == 0 { | if len(objectIDs) == 0 { | ||||
| return nil, nil | return nil, nil | ||||
| } | } | ||||
| @@ -85,6 +85,41 @@ func (db *ObjectDB) BatchGetByPackagePath(ctx SQLContext, pkgID cdssdk.PackageID | |||||
| return objs, nil | return objs, nil | ||||
| } | } | ||||
| // 仅返回查询到的对象 | |||||
| func (db *ObjectDB) BatchGetDetails(ctx SQLContext, objectIDs []cdssdk.ObjectID) ([]stgmod.ObjectDetail, error) { | |||||
| var objs []cdssdk.Object | |||||
| err := ctx.Table("Object").Where("ObjectID IN ?", objectIDs).Order("ObjectID ASC").Find(&objs).Error | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| // 获取所有的 ObjectBlock | |||||
| var allBlocks []stgmod.ObjectBlock | |||||
| err = ctx.Table("ObjectBlock").Where("ObjectID IN ?", objectIDs).Order("ObjectID, `Index` ASC").Find(&allBlocks).Error | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| // 获取所有的 PinnedObject | |||||
| var allPinnedObjs []cdssdk.PinnedObject | |||||
| err = ctx.Table("PinnedObject").Where("ObjectID IN ?", objectIDs).Order("ObjectID ASC").Find(&allPinnedObjs).Error | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| details := make([]stgmod.ObjectDetail, len(objs)) | |||||
| for i, obj := range objs { | |||||
| details[i] = stgmod.ObjectDetail{ | |||||
| Object: obj, | |||||
| } | |||||
| } | |||||
| stgmod.DetailsFillObjectBlocks(details, allBlocks) | |||||
| stgmod.DetailsFillPinnedAt(details, allPinnedObjs) | |||||
| return details, nil | |||||
| } | |||||
| func (db *ObjectDB) Create(ctx SQLContext, obj cdssdk.Object) (cdssdk.ObjectID, error) { | func (db *ObjectDB) Create(ctx SQLContext, obj cdssdk.Object) (cdssdk.ObjectID, error) { | ||||
| err := ctx.Table("Object").Create(&obj).Error | err := ctx.Table("Object").Create(&obj).Error | ||||
| if err != nil { | if err != nil { | ||||
| @@ -128,7 +163,7 @@ func (db *ObjectDB) BatchUpdateColumns(ctx SQLContext, objs []cdssdk.Object, col | |||||
| }).Create(objs).Error | }).Create(objs).Error | ||||
| } | } | ||||
| func (db *ObjectDB) GetPackageObjects(ctx SQLContext, packageID cdssdk.PackageID) ([]model.Object, error) { | |||||
| func (db *ObjectDB) GetPackageObjects(ctx SQLContext, packageID cdssdk.PackageID) ([]cdssdk.Object, error) { | |||||
| var ret []cdssdk.Object | var ret []cdssdk.Object | ||||
| err := ctx.Table("Object").Where("PackageID = ?", packageID).Order("ObjectID ASC").Find(&ret).Error | err := ctx.Table("Object").Where("PackageID = ?", packageID).Order("ObjectID ASC").Find(&ret).Error | ||||
| return ret, err | return ret, err | ||||
| @@ -33,6 +33,16 @@ func (db *ObjectBlockDB) BatchGetByObjectID(ctx SQLContext, objectIDs []cdssdk.O | |||||
| return blocks, err | return blocks, err | ||||
| } | } | ||||
| func (*ObjectBlockDB) GetInPackageID(ctx SQLContext, packageID cdssdk.PackageID) ([]stgmod.ObjectBlock, error) { | |||||
| var rets []stgmod.ObjectBlock | |||||
| err := ctx.Table("ObjectBlock"). | |||||
| Joins("INNER JOIN Object ON ObjectBlock.ObjectID = Object.ObjectID"). | |||||
| Where("Object.PackageID = ?", packageID). | |||||
| Order("ObjectBlock.ObjectID, ObjectBlock.`Index` ASC"). | |||||
| Find(&rets).Error | |||||
| return rets, err | |||||
| } | |||||
| func (db *ObjectBlockDB) Create(ctx SQLContext, objectID cdssdk.ObjectID, index int, stgID cdssdk.StorageID, fileHash cdssdk.FileHash) error { | func (db *ObjectBlockDB) Create(ctx SQLContext, objectID cdssdk.ObjectID, index int, stgID cdssdk.StorageID, fileHash cdssdk.FileHash) error { | ||||
| block := stgmod.ObjectBlock{ObjectID: objectID, Index: index, StorageID: stgID, FileHash: fileHash} | block := stgmod.ObjectBlock{ObjectID: objectID, Index: index, StorageID: stgID, FileHash: fileHash} | ||||
| return ctx.Table("ObjectBlock").Create(&block).Error | return ctx.Table("ObjectBlock").Create(&block).Error | ||||
| @@ -57,7 +57,7 @@ func (*PackageDB) BatchGetAllPackageIDs(ctx SQLContext, start int, count int) ([ | |||||
| return ret, err | return ret, err | ||||
| } | } | ||||
| func (db *PackageDB) GetBucketPackages(ctx SQLContext, userID cdssdk.UserID, bucketID cdssdk.BucketID) ([]model.Package, error) { | |||||
| func (db *PackageDB) GetUserBucketPackages(ctx SQLContext, userID cdssdk.UserID, bucketID cdssdk.BucketID) ([]model.Package, error) { | |||||
| var ret []model.Package | var ret []model.Package | ||||
| err := ctx.Table("UserBucket"). | err := ctx.Table("UserBucket"). | ||||
| Select("Package.*"). | Select("Package.*"). | ||||
| @@ -67,6 +67,15 @@ func (db *PackageDB) GetBucketPackages(ctx SQLContext, userID cdssdk.UserID, buc | |||||
| return ret, err | return ret, err | ||||
| } | } | ||||
| func (db *PackageDB) GetBucketPackages(ctx SQLContext, bucketID cdssdk.BucketID) ([]model.Package, error) { | |||||
| var ret []model.Package | |||||
| err := ctx.Table("Package"). | |||||
| Select("Package.*"). | |||||
| Where("BucketID = ?", bucketID). | |||||
| Find(&ret).Error | |||||
| return ret, err | |||||
| } | |||||
| // IsAvailable 判断一个用户是否拥有指定对象 | // IsAvailable 判断一个用户是否拥有指定对象 | ||||
| func (db *PackageDB) IsAvailable(ctx SQLContext, userID cdssdk.UserID, packageID cdssdk.PackageID) (bool, error) { | func (db *PackageDB) IsAvailable(ctx SQLContext, userID cdssdk.UserID, packageID cdssdk.PackageID) (bool, error) { | ||||
| var pkgID cdssdk.PackageID | var pkgID cdssdk.PackageID | ||||
| @@ -110,7 +119,7 @@ func (*PackageDB) GetUserPackageByName(ctx SQLContext, userID cdssdk.UserID, buc | |||||
| return ret, err | return ret, err | ||||
| } | } | ||||
| func (db *PackageDB) Create(ctx SQLContext, bucketID cdssdk.BucketID, name string) (cdssdk.PackageID, error) { | |||||
| func (db *PackageDB) Create(ctx SQLContext, bucketID cdssdk.BucketID, name string) (cdssdk.Package, error) { | |||||
| var packageID int64 | var packageID int64 | ||||
| err := ctx.Table("Package"). | err := ctx.Table("Package"). | ||||
| Select("PackageID"). | Select("PackageID"). | ||||
| @@ -118,33 +127,29 @@ func (db *PackageDB) Create(ctx SQLContext, bucketID cdssdk.BucketID, name strin | |||||
| Scan(&packageID).Error | Scan(&packageID).Error | ||||
| if err != nil { | if err != nil { | ||||
| return 0, err | |||||
| return cdssdk.Package{}, err | |||||
| } | } | ||||
| if packageID != 0 { | if packageID != 0 { | ||||
| return 0, gorm.ErrDuplicatedKey | |||||
| return cdssdk.Package{}, gorm.ErrDuplicatedKey | |||||
| } | } | ||||
| newPackage := cdssdk.Package{Name: name, BucketID: bucketID, State: cdssdk.PackageStateNormal} | newPackage := cdssdk.Package{Name: name, BucketID: bucketID, State: cdssdk.PackageStateNormal} | ||||
| if err := ctx.Create(&newPackage).Error; err != nil { | if err := ctx.Create(&newPackage).Error; err != nil { | ||||
| return 0, fmt.Errorf("insert package failed, err: %w", err) | |||||
| return cdssdk.Package{}, fmt.Errorf("insert package failed, err: %w", err) | |||||
| } | } | ||||
| return newPackage.PackageID, nil | |||||
| return newPackage, nil | |||||
| } | } | ||||
| // SoftDelete 设置一个对象被删除,并将相关数据删除 | |||||
| func (db *PackageDB) SoftDelete(ctx SQLContext, packageID cdssdk.PackageID) error { | |||||
| obj, err := db.GetByID(ctx, packageID) | |||||
| if err != nil { | |||||
| return fmt.Errorf("get package failed, err: %w", err) | |||||
| } | |||||
| if obj.State != cdssdk.PackageStateNormal { | |||||
| return nil | |||||
| } | |||||
| func (*PackageDB) Delete(ctx SQLContext, packageID cdssdk.PackageID) error { | |||||
| err := ctx.Delete(&model.Package{}, "PackageID = ?", packageID).Error | |||||
| return err | |||||
| } | |||||
| if err := db.ChangeState(ctx, packageID, cdssdk.PackageStateDeleted); err != nil { | |||||
| return fmt.Errorf("change package state failed, err: %w", err) | |||||
| // 删除与Package相关的所有数据 | |||||
| func (db *PackageDB) DeleteComplete(ctx SQLContext, packageID cdssdk.PackageID) error { | |||||
| if err := db.Package().Delete(ctx, packageID); err != nil { | |||||
| return fmt.Errorf("delete package state: %w", err) | |||||
| } | } | ||||
| if err := db.ObjectAccessStat().DeleteInPackage(ctx, packageID); err != nil { | if err := db.ObjectAccessStat().DeleteInPackage(ctx, packageID); err != nil { | ||||
| @@ -163,23 +168,13 @@ func (db *PackageDB) SoftDelete(ctx SQLContext, packageID cdssdk.PackageID) erro | |||||
| return fmt.Errorf("deleting objects in package: %w", err) | return fmt.Errorf("deleting objects in package: %w", err) | ||||
| } | } | ||||
| if _, err := db.StoragePackage().SetAllPackageDeleted(ctx, packageID); err != nil { | |||||
| return fmt.Errorf("set storage package deleted failed, err: %w", err) | |||||
| if err := db.PackageAccessStat().DeleteByPackageID(ctx, packageID); err != nil { | |||||
| return fmt.Errorf("deleting package access stat: %w", err) | |||||
| } | } | ||||
| return nil | return nil | ||||
| } | } | ||||
| // DeleteUnused 删除一个已经是Deleted状态,且不再被使用的对象 | |||||
| func (PackageDB) DeleteUnused(ctx SQLContext, packageID cdssdk.PackageID) error { | |||||
| err := ctx.Exec("DELETE FROM Package WHERE PackageID = ? AND State = ? AND NOT EXISTS (SELECT StorageID FROM StoragePackage WHERE PackageID = ?)", | |||||
| packageID, | |||||
| cdssdk.PackageStateDeleted, | |||||
| packageID, | |||||
| ).Error | |||||
| return err | |||||
| } | |||||
| func (*PackageDB) ChangeState(ctx SQLContext, packageID cdssdk.PackageID, state string) error { | func (*PackageDB) ChangeState(ctx SQLContext, packageID cdssdk.PackageID, state string) error { | ||||
| err := ctx.Exec("UPDATE Package SET State = ? WHERE PackageID = ?", state, packageID).Error | err := ctx.Exec("UPDATE Package SET State = ? WHERE PackageID = ?", state, packageID).Error | ||||
| return err | return err | ||||
| @@ -42,8 +42,10 @@ func (*PinnedObjectDB) BatchGetByObjectID(ctx SQLContext, objectIDs []cdssdk.Obj | |||||
| } | } | ||||
| func (*PinnedObjectDB) TryCreate(ctx SQLContext, stgID cdssdk.StorageID, objectID cdssdk.ObjectID, createTime time.Time) error { | func (*PinnedObjectDB) TryCreate(ctx SQLContext, stgID cdssdk.StorageID, objectID cdssdk.ObjectID, createTime time.Time) error { | ||||
| err := ctx.Clauses(clause.Insert{Modifier: "ignore"}).Table("PinnedObject").Create(&cdssdk.PinnedObject{StorageID: stgID, ObjectID: objectID, CreateTime: createTime}).Error | |||||
| return err | |||||
| return ctx.Clauses(clause.OnConflict{ | |||||
| Columns: []clause.Column{{Name: "ObjectID"}, {Name: "StorageID"}}, | |||||
| DoUpdates: clause.AssignmentColumns([]string{"CreateTime"}), | |||||
| }).Create(&cdssdk.PinnedObject{StorageID: stgID, ObjectID: objectID, CreateTime: createTime}).Error | |||||
| } | } | ||||
| func (*PinnedObjectDB) BatchTryCreate(ctx SQLContext, pinneds []cdssdk.PinnedObject) error { | func (*PinnedObjectDB) BatchTryCreate(ctx SQLContext, pinneds []cdssdk.PinnedObject) error { | ||||
| @@ -51,8 +53,10 @@ func (*PinnedObjectDB) BatchTryCreate(ctx SQLContext, pinneds []cdssdk.PinnedObj | |||||
| return nil | return nil | ||||
| } | } | ||||
| err := ctx.Clauses(clause.Insert{Modifier: "ignore"}).Table("PinnedObject").Create(pinneds).Error | |||||
| return err | |||||
| return ctx.Clauses(clause.OnConflict{ | |||||
| Columns: []clause.Column{{Name: "ObjectID"}, {Name: "StorageID"}}, | |||||
| DoUpdates: clause.AssignmentColumns([]string{"CreateTime"}), | |||||
| }).Create(&pinneds).Error | |||||
| } | } | ||||
| func (*PinnedObjectDB) CreateFromPackage(ctx SQLContext, packageID cdssdk.PackageID, stgID cdssdk.StorageID) error { | func (*PinnedObjectDB) CreateFromPackage(ctx SQLContext, packageID cdssdk.PackageID, stgID cdssdk.StorageID) error { | ||||
| @@ -1,83 +0,0 @@ | |||||
| package db2 | |||||
| import ( | |||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/db2/model" | |||||
| ) | |||||
| type StoragePackageDB struct { | |||||
| *DB | |||||
| } | |||||
| func (db *DB) StoragePackage() *StoragePackageDB { | |||||
| return &StoragePackageDB{DB: db} | |||||
| } | |||||
| func (*StoragePackageDB) Get(ctx SQLContext, storageID cdssdk.StorageID, packageID cdssdk.PackageID, userID cdssdk.UserID) (model.StoragePackage, error) { | |||||
| var ret model.StoragePackage | |||||
| err := ctx.Table("StoragePackage").Where("StorageID = ? AND PackageID = ? AND UserID = ?", storageID, packageID, userID).First(&ret).Error | |||||
| return ret, err | |||||
| } | |||||
| func (*StoragePackageDB) GetAllByStorageAndPackageID(ctx SQLContext, storageID cdssdk.StorageID, packageID cdssdk.PackageID) ([]model.StoragePackage, error) { | |||||
| var ret []model.StoragePackage | |||||
| err := ctx.Table("StoragePackage").Where("StorageID = ? AND PackageID = ?", storageID, packageID).Find(&ret).Error | |||||
| return ret, err | |||||
| } | |||||
| func (*StoragePackageDB) GetAllByStorageID(ctx SQLContext, storageID cdssdk.StorageID) ([]model.StoragePackage, error) { | |||||
| var ret []model.StoragePackage | |||||
| err := ctx.Table("StoragePackage").Where("StorageID = ?", storageID).Find(&ret).Error | |||||
| return ret, err | |||||
| } | |||||
| func (*StoragePackageDB) CreateOrUpdate(ctx SQLContext, storageID cdssdk.StorageID, packageID cdssdk.PackageID, userID cdssdk.UserID) error { | |||||
| sql := "INSERT INTO StoragePackage (StorageID, PackageID, UserID, State) VALUES (?, ?, ?, ?) " + | |||||
| "ON DUPLICATE KEY UPDATE State = VALUES(State)" | |||||
| return ctx.Exec(sql, storageID, packageID, userID, model.StoragePackageStateNormal).Error | |||||
| } | |||||
| func (*StoragePackageDB) ChangeState(ctx SQLContext, storageID cdssdk.StorageID, packageID cdssdk.PackageID, userID cdssdk.UserID, state string) error { | |||||
| return ctx.Table("StoragePackage").Where("StorageID = ? AND PackageID = ? AND UserID = ?", storageID, packageID, userID).Update("State", state).Error | |||||
| } | |||||
| // SetStateNormal 将状态设置为Normal,如果记录状态是Deleted,则不进行操作 | |||||
| func (*StoragePackageDB) SetStateNormal(ctx SQLContext, storageID cdssdk.StorageID, packageID cdssdk.PackageID, userID cdssdk.UserID) error { | |||||
| return ctx.Table("StoragePackage").Where("StorageID = ? AND PackageID = ? AND UserID = ? AND State <> ?", | |||||
| storageID, packageID, userID, model.StoragePackageStateDeleted).Update("State", model.StoragePackageStateNormal).Error | |||||
| } | |||||
| func (*StoragePackageDB) SetAllPackageState(ctx SQLContext, packageID cdssdk.PackageID, state string) (int64, error) { | |||||
| ret := ctx.Table("StoragePackage").Where("PackageID = ?", packageID).Update("State", state) | |||||
| if err := ret.Error; err != nil { | |||||
| return 0, err | |||||
| } | |||||
| return ret.RowsAffected, nil | |||||
| } | |||||
| // SetAllPackageOutdated 将Storage中指定对象设置为已过期。只会设置Normal状态的对象 | |||||
| func (*StoragePackageDB) SetAllPackageOutdated(ctx SQLContext, packageID cdssdk.PackageID) (int64, error) { | |||||
| ret := ctx.Table("StoragePackage").Where("State = ? AND PackageID = ?", model.StoragePackageStateNormal, packageID).Update("State", model.StoragePackageStateOutdated) | |||||
| if err := ret.Error; err != nil { | |||||
| return 0, err | |||||
| } | |||||
| return ret.RowsAffected, nil | |||||
| } | |||||
| func (db *StoragePackageDB) SetAllPackageDeleted(ctx SQLContext, packageID cdssdk.PackageID) (int64, error) { | |||||
| return db.SetAllPackageState(ctx, packageID, model.StoragePackageStateDeleted) | |||||
| } | |||||
| func (*StoragePackageDB) Delete(ctx SQLContext, storageID cdssdk.StorageID, packageID cdssdk.PackageID, userID cdssdk.UserID) error { | |||||
| return ctx.Table("StoragePackage").Where("StorageID = ? AND PackageID = ? AND UserID = ?", storageID, packageID, userID).Delete(&model.StoragePackage{}).Error | |||||
| } | |||||
| // FindPackageStorages 查询存储了指定对象的Storage | |||||
| func (*StoragePackageDB) FindPackageStorages(ctx SQLContext, packageID cdssdk.PackageID) ([]model.Storage, error) { | |||||
| var ret []model.Storage | |||||
| err := ctx.Table("StoragePackage").Select("Storage.*"). | |||||
| Joins("JOIN Storage ON StoragePackage.StorageID = Storage.StorageID"). | |||||
| Where("PackageID = ?", packageID). | |||||
| Scan(&ret).Error | |||||
| return ret, err | |||||
| } | |||||
| @@ -13,10 +13,14 @@ func (db *DB) UserBucket() *UserBucketDB { | |||||
| return &UserBucketDB{DB: db} | return &UserBucketDB{DB: db} | ||||
| } | } | ||||
| func (*UserBucketDB) Create(ctx SQLContext, userID int64, bucketID int64) error { | |||||
| func (*UserBucketDB) Create(ctx SQLContext, userID cdssdk.UserID, bucketID cdssdk.BucketID) error { | |||||
| userBucket := model.UserBucket{ | userBucket := model.UserBucket{ | ||||
| UserID: cdssdk.UserID(userID), | |||||
| BucketID: cdssdk.BucketID(bucketID), | |||||
| UserID: userID, | |||||
| BucketID: bucketID, | |||||
| } | } | ||||
| return ctx.Table("UserBucket").Create(&userBucket).Error | return ctx.Table("UserBucket").Create(&userBucket).Error | ||||
| } | } | ||||
| func (*UserBucketDB) DeleteByBucketID(ctx SQLContext, bucketID cdssdk.BucketID) error { | |||||
| return ctx.Table("UserBucket").Where("BucketID = ?", bucketID).Delete(&model.UserBucket{}).Error | |||||
| } | |||||
| @@ -1,24 +0,0 @@ | |||||
| package reqbuilder | |||||
| import ( | |||||
| "gitlink.org.cn/cloudream/common/pkgs/distlock" | |||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/distlock/lockprovider" | |||||
| ) | |||||
| type MetadataStoragePackageLockReqBuilder struct { | |||||
| *MetadataLockReqBuilder | |||||
| } | |||||
| func (b *MetadataLockReqBuilder) StoragePackage() *MetadataStoragePackageLockReqBuilder { | |||||
| return &MetadataStoragePackageLockReqBuilder{MetadataLockReqBuilder: b} | |||||
| } | |||||
| func (b *MetadataStoragePackageLockReqBuilder) CreateOne(userID cdssdk.UserID, storageID cdssdk.StorageID, packageID cdssdk.PackageID) *MetadataStoragePackageLockReqBuilder { | |||||
| b.locks = append(b.locks, distlock.Lock{ | |||||
| Path: b.makePath("StoragePackage"), | |||||
| Name: lockprovider.MetadataCreateLock, | |||||
| Target: *lockprovider.NewStringLockTarget().Add(userID, storageID, packageID), | |||||
| }) | |||||
| return b | |||||
| } | |||||
| @@ -24,7 +24,7 @@ func initProviders() []distlock.PathProvider { | |||||
| provs = append(provs, initMetadataLockProviders()...) | provs = append(provs, initMetadataLockProviders()...) | ||||
| provs = append(provs, initIPFSLockProviders()...) | |||||
| provs = append(provs, initShardLockProviders()...) | |||||
| provs = append(provs, initStorageLockProviders()...) | provs = append(provs, initStorageLockProviders()...) | ||||
| @@ -45,12 +45,11 @@ func initMetadataLockProviders() []distlock.PathProvider { | |||||
| distlock.NewPathProvider(lockprovider.NewMetadataLock(), lockprovider.MetadataLockPathPrefix, "ObjectRep"), | distlock.NewPathProvider(lockprovider.NewMetadataLock(), lockprovider.MetadataLockPathPrefix, "ObjectRep"), | ||||
| distlock.NewPathProvider(lockprovider.NewMetadataLock(), lockprovider.MetadataLockPathPrefix, "ObjectBlock"), | distlock.NewPathProvider(lockprovider.NewMetadataLock(), lockprovider.MetadataLockPathPrefix, "ObjectBlock"), | ||||
| distlock.NewPathProvider(lockprovider.NewMetadataLock(), lockprovider.MetadataLockPathPrefix, "Cache"), | distlock.NewPathProvider(lockprovider.NewMetadataLock(), lockprovider.MetadataLockPathPrefix, "Cache"), | ||||
| distlock.NewPathProvider(lockprovider.NewMetadataLock(), lockprovider.MetadataLockPathPrefix, "StoragePackage"), | |||||
| distlock.NewPathProvider(lockprovider.NewMetadataLock(), lockprovider.MetadataLockPathPrefix, "Location"), | distlock.NewPathProvider(lockprovider.NewMetadataLock(), lockprovider.MetadataLockPathPrefix, "Location"), | ||||
| } | } | ||||
| } | } | ||||
| func initIPFSLockProviders() []distlock.PathProvider { | |||||
| func initShardLockProviders() []distlock.PathProvider { | |||||
| return []distlock.PathProvider{ | return []distlock.PathProvider{ | ||||
| distlock.NewPathProvider(lockprovider.NewShardStoreLock(), lockprovider.ShardStoreLockPathPrefix, trie.WORD_ANY), | distlock.NewPathProvider(lockprovider.NewShardStoreLock(), lockprovider.ShardStoreLockPathPrefix, trie.WORD_ANY), | ||||
| } | } | ||||
| @@ -3,8 +3,6 @@ package downloader | |||||
| type Config struct { | type Config struct { | ||||
| // EC模式的Object的条带缓存数量 | // EC模式的Object的条带缓存数量 | ||||
| MaxStripCacheCount int `json:"maxStripCacheCount"` | MaxStripCacheCount int `json:"maxStripCacheCount"` | ||||
| // 当到下载节点的延迟高于这个值时,该节点在评估时会有更高的分数惩罚,单位:ms | |||||
| HighLatencyHubMs float64 `json:"highLatencyHubMs"` | |||||
| // EC模式下,每个Object的条带的预取数量,最少为1 | // EC模式下,每个Object的条带的预取数量,最少为1 | ||||
| ECStripPrefetchCount int `json:"ecStripPrefetchCount"` | ECStripPrefetchCount int `json:"ecStripPrefetchCount"` | ||||
| } | } | ||||
| @@ -10,8 +10,9 @@ import ( | |||||
| stgglb "gitlink.org.cn/cloudream/storage/common/globals" | stgglb "gitlink.org.cn/cloudream/storage/common/globals" | ||||
| stgmod "gitlink.org.cn/cloudream/storage/common/models" | stgmod "gitlink.org.cn/cloudream/storage/common/models" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/connectivity" | "gitlink.org.cn/cloudream/storage/common/pkgs/connectivity" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/downloader/strategy" | |||||
| coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator" | coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/svcmgr" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/agtpool" | |||||
| ) | ) | ||||
| const ( | const ( | ||||
| @@ -38,23 +39,25 @@ type Downloading struct { | |||||
| } | } | ||||
| type Downloader struct { | type Downloader struct { | ||||
| strips *StripCache | |||||
| cfg Config | |||||
| conn *connectivity.Collector | |||||
| stgMgr *svcmgr.Manager | |||||
| strips *StripCache | |||||
| cfg Config | |||||
| conn *connectivity.Collector | |||||
| stgAgts *agtpool.AgentPool | |||||
| selector *strategy.Selector | |||||
| } | } | ||||
| func NewDownloader(cfg Config, conn *connectivity.Collector, stgMgr *svcmgr.Manager) Downloader { | |||||
| func NewDownloader(cfg Config, conn *connectivity.Collector, stgAgts *agtpool.AgentPool, sel *strategy.Selector) Downloader { | |||||
| if cfg.MaxStripCacheCount == 0 { | if cfg.MaxStripCacheCount == 0 { | ||||
| cfg.MaxStripCacheCount = DefaultMaxStripCacheCount | cfg.MaxStripCacheCount = DefaultMaxStripCacheCount | ||||
| } | } | ||||
| ch, _ := lru.New[ECStripKey, ObjectECStrip](cfg.MaxStripCacheCount) | ch, _ := lru.New[ECStripKey, ObjectECStrip](cfg.MaxStripCacheCount) | ||||
| return Downloader{ | return Downloader{ | ||||
| strips: ch, | |||||
| cfg: cfg, | |||||
| conn: conn, | |||||
| stgMgr: stgMgr, | |||||
| strips: ch, | |||||
| cfg: cfg, | |||||
| conn: conn, | |||||
| stgAgts: stgAgts, | |||||
| selector: sel, | |||||
| } | } | ||||
| } | } | ||||
| @@ -4,28 +4,21 @@ import ( | |||||
| "context" | "context" | ||||
| "fmt" | "fmt" | ||||
| "io" | "io" | ||||
| "math" | |||||
| "reflect" | "reflect" | ||||
| "time" | |||||
| "github.com/samber/lo" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/bitmap" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/logger" | "gitlink.org.cn/cloudream/common/pkgs/logger" | ||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | ||||
| "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/math2" | ||||
| "gitlink.org.cn/cloudream/common/utils/sort2" | |||||
| "gitlink.org.cn/cloudream/storage/common/consts" | |||||
| stgglb "gitlink.org.cn/cloudream/storage/common/globals" | stgglb "gitlink.org.cn/cloudream/storage/common/globals" | ||||
| stgmod "gitlink.org.cn/cloudream/storage/common/models" | stgmod "gitlink.org.cn/cloudream/storage/common/models" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/distlock" | "gitlink.org.cn/cloudream/storage/common/pkgs/distlock" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/downloader/strategy" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2" | "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/parser" | "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/parser" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/iterator" | "gitlink.org.cn/cloudream/storage/common/pkgs/iterator" | ||||
| coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator" | |||||
| ) | ) | ||||
| type downloadStorageInfo struct { | type downloadStorageInfo struct { | ||||
| @@ -39,15 +32,10 @@ type DownloadContext struct { | |||||
| Distlock *distlock.Service | Distlock *distlock.Service | ||||
| } | } | ||||
| type DownloadObjectIterator struct { | type DownloadObjectIterator struct { | ||||
| OnClosing func() | |||||
| OnClosing func() | |||||
| downloader *Downloader | downloader *Downloader | ||||
| reqs []downloadReqeust2 | reqs []downloadReqeust2 | ||||
| currentIndex int | currentIndex int | ||||
| inited bool | |||||
| coorCli *coormq.Client | |||||
| allStorages map[cdssdk.StorageID]stgmod.StorageDetail | |||||
| } | } | ||||
| func NewDownloadObjectIterator(downloader *Downloader, downloadObjs []downloadReqeust2) *DownloadObjectIterator { | func NewDownloadObjectIterator(downloader *Downloader, downloadObjs []downloadReqeust2) *DownloadObjectIterator { | ||||
| @@ -58,68 +46,11 @@ func NewDownloadObjectIterator(downloader *Downloader, downloadObjs []downloadRe | |||||
| } | } | ||||
| func (i *DownloadObjectIterator) MoveNext() (*Downloading, error) { | func (i *DownloadObjectIterator) MoveNext() (*Downloading, error) { | ||||
| if !i.inited { | |||||
| if err := i.init(); err != nil { | |||||
| return nil, err | |||||
| } | |||||
| i.inited = true | |||||
| } | |||||
| if i.currentIndex >= len(i.reqs) { | if i.currentIndex >= len(i.reqs) { | ||||
| return nil, iterator.ErrNoMoreItem | return nil, iterator.ErrNoMoreItem | ||||
| } | } | ||||
| item, err := i.doMove() | |||||
| i.currentIndex++ | |||||
| return item, err | |||||
| } | |||||
| func (i *DownloadObjectIterator) init() error { | |||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | |||||
| if err != nil { | |||||
| return fmt.Errorf("new coordinator client: %w", err) | |||||
| } | |||||
| i.coorCli = coorCli | |||||
| allStgIDsMp := make(map[cdssdk.StorageID]bool) | |||||
| for _, obj := range i.reqs { | |||||
| if obj.Detail == nil { | |||||
| continue | |||||
| } | |||||
| for _, p := range obj.Detail.PinnedAt { | |||||
| allStgIDsMp[p] = true | |||||
| } | |||||
| for _, b := range obj.Detail.Blocks { | |||||
| allStgIDsMp[b.StorageID] = true | |||||
| } | |||||
| } | |||||
| stgIDs := lo.Keys(allStgIDsMp) | |||||
| getStgs, err := coorCli.GetStorageDetails(coormq.ReqGetStorageDetails(stgIDs)) | |||||
| if err != nil { | |||||
| return fmt.Errorf("getting storage details: %w", err) | |||||
| } | |||||
| i.allStorages = make(map[cdssdk.StorageID]stgmod.StorageDetail) | |||||
| for idx, s := range getStgs.Storages { | |||||
| if s == nil { | |||||
| return fmt.Errorf("storage %v not found", stgIDs[idx]) | |||||
| } | |||||
| if s.Storage.ShardStore == nil { | |||||
| return fmt.Errorf("storage %v has no shard store", stgIDs[idx]) | |||||
| } | |||||
| i.allStorages[s.Storage.StorageID] = *s | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func (iter *DownloadObjectIterator) doMove() (*Downloading, error) { | |||||
| req := iter.reqs[iter.currentIndex] | |||||
| req := i.reqs[i.currentIndex] | |||||
| if req.Detail == nil { | if req.Detail == nil { | ||||
| return &Downloading{ | return &Downloading{ | ||||
| Object: nil, | Object: nil, | ||||
| @@ -128,57 +59,51 @@ func (iter *DownloadObjectIterator) doMove() (*Downloading, error) { | |||||
| }, nil | }, nil | ||||
| } | } | ||||
| switch red := req.Detail.Object.Redundancy.(type) { | |||||
| case *cdssdk.NoneRedundancy: | |||||
| reader, err := iter.downloadNoneOrRepObject(req) | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("downloading object %v: %w", req.Raw.ObjectID, err) | |||||
| } | |||||
| destHub := cdssdk.HubID(0) | |||||
| if stgglb.Local.HubID != nil { | |||||
| destHub = *stgglb.Local.HubID | |||||
| } | |||||
| return &Downloading{ | |||||
| Object: &req.Detail.Object, | |||||
| File: reader, | |||||
| Request: req.Raw, | |||||
| }, nil | |||||
| strg, err := i.downloader.selector.Select(strategy.Request{ | |||||
| Detail: *req.Detail, | |||||
| Range: math2.NewRange(req.Raw.Offset, req.Raw.Length), | |||||
| DestHub: destHub, | |||||
| DestLocation: stgglb.Local.LocationID, | |||||
| }) | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("selecting download strategy: %w", err) | |||||
| } | |||||
| case *cdssdk.RepRedundancy: | |||||
| reader, err := iter.downloadNoneOrRepObject(req) | |||||
| var reader io.ReadCloser | |||||
| switch strg := strg.(type) { | |||||
| case *strategy.DirectStrategy: | |||||
| reader, err = i.downloadDirect(req, *strg) | |||||
| if err != nil { | if err != nil { | ||||
| return nil, fmt.Errorf("downloading rep object %v: %w", req.Raw.ObjectID, err) | |||||
| return nil, fmt.Errorf("downloading object %v: %w", req.Raw.ObjectID, err) | |||||
| } | } | ||||
| return &Downloading{ | |||||
| Object: &req.Detail.Object, | |||||
| File: reader, | |||||
| Request: req.Raw, | |||||
| }, nil | |||||
| case *cdssdk.ECRedundancy: | |||||
| reader, err := iter.downloadECObject(req, red) | |||||
| case *strategy.ECReconstructStrategy: | |||||
| reader, err = i.downloadECReconstruct(req, *strg) | |||||
| if err != nil { | if err != nil { | ||||
| return nil, fmt.Errorf("downloading ec object %v: %w", req.Raw.ObjectID, err) | return nil, fmt.Errorf("downloading ec object %v: %w", req.Raw.ObjectID, err) | ||||
| } | } | ||||
| return &Downloading{ | |||||
| Object: &req.Detail.Object, | |||||
| File: reader, | |||||
| Request: req.Raw, | |||||
| }, nil | |||||
| case *cdssdk.LRCRedundancy: | |||||
| reader, err := iter.downloadLRCObject(req, red) | |||||
| case *strategy.LRCReconstructStrategy: | |||||
| reader, err = i.downloadLRCReconstruct(req, *strg) | |||||
| if err != nil { | if err != nil { | ||||
| return nil, fmt.Errorf("downloading lrc object %v: %w", req.Raw.ObjectID, err) | return nil, fmt.Errorf("downloading lrc object %v: %w", req.Raw.ObjectID, err) | ||||
| } | } | ||||
| return &Downloading{ | |||||
| Object: &req.Detail.Object, | |||||
| File: reader, | |||||
| Request: req.Raw, | |||||
| }, nil | |||||
| default: | |||||
| return nil, fmt.Errorf("unsupported strategy type: %v", reflect.TypeOf(strg)) | |||||
| } | } | ||||
| return nil, fmt.Errorf("unsupported redundancy type: %v of object %v", reflect.TypeOf(req.Detail.Object.Redundancy), req.Raw.ObjectID) | |||||
| i.currentIndex++ | |||||
| return &Downloading{ | |||||
| Object: &req.Detail.Object, | |||||
| File: reader, | |||||
| Request: req.Raw, | |||||
| }, nil | |||||
| } | } | ||||
| func (i *DownloadObjectIterator) Close() { | func (i *DownloadObjectIterator) Close() { | ||||
| @@ -187,227 +112,93 @@ func (i *DownloadObjectIterator) Close() { | |||||
| } | } | ||||
| } | } | ||||
| func (iter *DownloadObjectIterator) downloadNoneOrRepObject(obj downloadReqeust2) (io.ReadCloser, error) { | |||||
| allStgs, err := iter.sortDownloadStorages(obj) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| func (i *DownloadObjectIterator) downloadDirect(req downloadReqeust2, strg strategy.DirectStrategy) (io.ReadCloser, error) { | |||||
| logger.Debugf("downloading object %v from storage %v", req.Raw.ObjectID, strg.Storage.Storage.String()) | |||||
| bsc, blocks := iter.getMinReadingBlockSolution(allStgs, 1) | |||||
| osc, stg := iter.getMinReadingObjectSolution(allStgs, 1) | |||||
| if bsc < osc { | |||||
| logger.Debugf("downloading object %v from storage %v", obj.Raw.ObjectID, blocks[0].Storage.Storage.String()) | |||||
| return iter.downloadFromStorage(&blocks[0].Storage, obj) | |||||
| } | |||||
| var strHandle *exec.DriverReadStream | |||||
| ft := ioswitch2.NewFromTo() | |||||
| if osc == math.MaxFloat64 { | |||||
| // bsc >= osc,如果osc是MaxFloat64,那么bsc也一定是,也就意味着没有足够块来恢复文件 | |||||
| return nil, fmt.Errorf("no storage has this object") | |||||
| toExec, handle := ioswitch2.NewToDriver(ioswitch2.RawStream()) | |||||
| toExec.Range = math2.Range{ | |||||
| Offset: req.Raw.Offset, | |||||
| } | } | ||||
| logger.Debugf("downloading object %v from storage %v", obj.Raw.ObjectID, stg.Storage.String()) | |||||
| return iter.downloadFromStorage(stg, obj) | |||||
| } | |||||
| func (iter *DownloadObjectIterator) downloadECObject(req downloadReqeust2, ecRed *cdssdk.ECRedundancy) (io.ReadCloser, error) { | |||||
| allStorages, err := iter.sortDownloadStorages(req) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| if req.Raw.Length != -1 { | |||||
| len := req.Raw.Length | |||||
| toExec.Range.Length = &len | |||||
| } | } | ||||
| bsc, blocks := iter.getMinReadingBlockSolution(allStorages, ecRed.K) | |||||
| osc, stg := iter.getMinReadingObjectSolution(allStorages, ecRed.K) | |||||
| if bsc < osc { | |||||
| var logStrs []any = []any{fmt.Sprintf("downloading ec object %v from blocks: ", req.Raw.ObjectID)} | |||||
| for i, b := range blocks { | |||||
| if i > 0 { | |||||
| logStrs = append(logStrs, ", ") | |||||
| } | |||||
| logStrs = append(logStrs, fmt.Sprintf("%v@%v", b.Block.Index, b.Storage.Storage.String())) | |||||
| } | |||||
| logger.Debug(logStrs...) | |||||
| pr, pw := io.Pipe() | |||||
| go func() { | |||||
| readPos := req.Raw.Offset | |||||
| totalReadLen := req.Detail.Object.Size - req.Raw.Offset | |||||
| if req.Raw.Length >= 0 { | |||||
| totalReadLen = math2.Min(req.Raw.Length, totalReadLen) | |||||
| } | |||||
| firstStripIndex := readPos / ecRed.StripSize() | |||||
| stripIter := NewStripIterator(iter.downloader, req.Detail.Object, blocks, ecRed, firstStripIndex, iter.downloader.strips, iter.downloader.cfg.ECStripPrefetchCount) | |||||
| defer stripIter.Close() | |||||
| for totalReadLen > 0 { | |||||
| strip, err := stripIter.MoveNext() | |||||
| if err == iterator.ErrNoMoreItem { | |||||
| pw.CloseWithError(io.ErrUnexpectedEOF) | |||||
| return | |||||
| } | |||||
| if err != nil { | |||||
| pw.CloseWithError(err) | |||||
| return | |||||
| } | |||||
| readRelativePos := readPos - strip.Position | |||||
| curReadLen := math2.Min(totalReadLen, ecRed.StripSize()-readRelativePos) | |||||
| err = io2.WriteAll(pw, strip.Data[readRelativePos:readRelativePos+curReadLen]) | |||||
| if err != nil { | |||||
| pw.CloseWithError(err) | |||||
| return | |||||
| } | |||||
| totalReadLen -= curReadLen | |||||
| readPos += curReadLen | |||||
| } | |||||
| pw.Close() | |||||
| }() | |||||
| ft.AddFrom(ioswitch2.NewFromShardstore(req.Detail.Object.FileHash, *strg.Storage.MasterHub, strg.Storage, ioswitch2.RawStream())).AddTo(toExec) | |||||
| strHandle = handle | |||||
| return pr, nil | |||||
| plans := exec.NewPlanBuilder() | |||||
| if err := parser.Parse(ft, plans); err != nil { | |||||
| return nil, fmt.Errorf("parsing plan: %w", err) | |||||
| } | } | ||||
| // bsc >= osc,如果osc是MaxFloat64,那么bsc也一定是,也就意味着没有足够块来恢复文件 | |||||
| if osc == math.MaxFloat64 { | |||||
| return nil, fmt.Errorf("no enough blocks to reconstruct the object %v , want %d, get only %d", req.Raw.ObjectID, ecRed.K, len(blocks)) | |||||
| } | |||||
| exeCtx := exec.NewExecContext() | |||||
| exec.SetValueByType(exeCtx, i.downloader.stgAgts) | |||||
| exec := plans.Execute(exeCtx) | |||||
| go exec.Wait(context.TODO()) | |||||
| logger.Debugf("downloading ec object %v from storage %v", req.Raw.ObjectID, stg.Storage.String()) | |||||
| return iter.downloadFromStorage(stg, req) | |||||
| return exec.BeginRead(strHandle) | |||||
| } | } | ||||
| func (iter *DownloadObjectIterator) sortDownloadStorages(req downloadReqeust2) ([]*downloadStorageInfo, error) { | |||||
| var stgIDs []cdssdk.StorageID | |||||
| for _, id := range req.Detail.PinnedAt { | |||||
| if !lo.Contains(stgIDs, id) { | |||||
| stgIDs = append(stgIDs, id) | |||||
| } | |||||
| } | |||||
| for _, b := range req.Detail.Blocks { | |||||
| if !lo.Contains(stgIDs, b.StorageID) { | |||||
| stgIDs = append(stgIDs, b.StorageID) | |||||
| func (i *DownloadObjectIterator) downloadECReconstruct(req downloadReqeust2, strg strategy.ECReconstructStrategy) (io.ReadCloser, error) { | |||||
| var logStrs []any = []any{fmt.Sprintf("downloading ec object %v from: ", req.Raw.ObjectID)} | |||||
| for i, b := range strg.Blocks { | |||||
| if i > 0 { | |||||
| logStrs = append(logStrs, ", ") | |||||
| } | } | ||||
| logStrs = append(logStrs, fmt.Sprintf("%v@%v", b.Index, strg.Storages[i].Storage.String())) | |||||
| } | } | ||||
| logger.Debug(logStrs...) | |||||
| downloadStorageMap := make(map[cdssdk.StorageID]*downloadStorageInfo) | |||||
| for _, id := range req.Detail.PinnedAt { | |||||
| storage, ok := downloadStorageMap[id] | |||||
| if !ok { | |||||
| mod := iter.allStorages[id] | |||||
| storage = &downloadStorageInfo{ | |||||
| Storage: mod, | |||||
| ObjectPinned: true, | |||||
| Distance: iter.getStorageDistance(mod), | |||||
| } | |||||
| downloadStorageMap[id] = storage | |||||
| downloadBlks := make([]downloadBlock, len(strg.Blocks)) | |||||
| for i, b := range strg.Blocks { | |||||
| downloadBlks[i] = downloadBlock{ | |||||
| Block: b, | |||||
| Storage: strg.Storages[i], | |||||
| } | } | ||||
| storage.ObjectPinned = true | |||||
| } | } | ||||
| for _, b := range req.Detail.Blocks { | |||||
| storage, ok := downloadStorageMap[b.StorageID] | |||||
| if !ok { | |||||
| mod := iter.allStorages[b.StorageID] | |||||
| storage = &downloadStorageInfo{ | |||||
| Storage: mod, | |||||
| Distance: iter.getStorageDistance(mod), | |||||
| } | |||||
| downloadStorageMap[b.StorageID] = storage | |||||
| pr, pw := io.Pipe() | |||||
| go func() { | |||||
| readPos := req.Raw.Offset | |||||
| totalReadLen := req.Detail.Object.Size - req.Raw.Offset | |||||
| if req.Raw.Length >= 0 { | |||||
| totalReadLen = math2.Min(req.Raw.Length, totalReadLen) | |||||
| } | } | ||||
| storage.Blocks = append(storage.Blocks, b) | |||||
| } | |||||
| return sort2.Sort(lo.Values(downloadStorageMap), func(left, right *downloadStorageInfo) int { | |||||
| return sort2.Cmp(left.Distance, right.Distance) | |||||
| }), nil | |||||
| } | |||||
| firstStripIndex := readPos / strg.Redundancy.StripSize() | |||||
| stripIter := NewStripIterator(i.downloader, req.Detail.Object, downloadBlks, strg.Redundancy, firstStripIndex, i.downloader.strips, i.downloader.cfg.ECStripPrefetchCount) | |||||
| defer stripIter.Close() | |||||
| func (iter *DownloadObjectIterator) getMinReadingBlockSolution(sortedStgs []*downloadStorageInfo, k int) (float64, []downloadBlock) { | |||||
| gotBlocksMap := bitmap.Bitmap64(0) | |||||
| var gotBlocks []downloadBlock | |||||
| dist := float64(0.0) | |||||
| for _, n := range sortedStgs { | |||||
| for _, b := range n.Blocks { | |||||
| if !gotBlocksMap.Get(b.Index) { | |||||
| gotBlocks = append(gotBlocks, downloadBlock{ | |||||
| Storage: n.Storage, | |||||
| Block: b, | |||||
| }) | |||||
| gotBlocksMap.Set(b.Index, true) | |||||
| dist += n.Distance | |||||
| for totalReadLen > 0 { | |||||
| strip, err := stripIter.MoveNext() | |||||
| if err == iterator.ErrNoMoreItem { | |||||
| pw.CloseWithError(io.ErrUnexpectedEOF) | |||||
| return | |||||
| } | } | ||||
| if len(gotBlocks) >= k { | |||||
| return dist, gotBlocks | |||||
| if err != nil { | |||||
| pw.CloseWithError(err) | |||||
| return | |||||
| } | } | ||||
| } | |||||
| } | |||||
| return math.MaxFloat64, gotBlocks | |||||
| } | |||||
| func (iter *DownloadObjectIterator) getMinReadingObjectSolution(sortedStgs []*downloadStorageInfo, k int) (float64, *stgmod.StorageDetail) { | |||||
| dist := math.MaxFloat64 | |||||
| var downloadStg *stgmod.StorageDetail | |||||
| for _, n := range sortedStgs { | |||||
| if n.ObjectPinned && float64(k)*n.Distance < dist { | |||||
| dist = float64(k) * n.Distance | |||||
| stg := n.Storage | |||||
| downloadStg = &stg | |||||
| } | |||||
| } | |||||
| readRelativePos := readPos - strip.Position | |||||
| curReadLen := math2.Min(totalReadLen, strg.Redundancy.StripSize()-readRelativePos) | |||||
| return dist, downloadStg | |||||
| } | |||||
| err = io2.WriteAll(pw, strip.Data[readRelativePos:readRelativePos+curReadLen]) | |||||
| if err != nil { | |||||
| pw.CloseWithError(err) | |||||
| return | |||||
| } | |||||
| func (iter *DownloadObjectIterator) getStorageDistance(stg stgmod.StorageDetail) float64 { | |||||
| if stgglb.Local.HubID != nil { | |||||
| if stg.MasterHub.HubID == *stgglb.Local.HubID { | |||||
| return consts.StorageDistanceSameStorage | |||||
| totalReadLen -= curReadLen | |||||
| readPos += curReadLen | |||||
| } | } | ||||
| } | |||||
| if stg.MasterHub.LocationID == stgglb.Local.LocationID { | |||||
| return consts.StorageDistanceSameLocation | |||||
| } | |||||
| c := iter.downloader.conn.Get(stg.MasterHub.HubID) | |||||
| if c == nil || c.Delay == nil || *c.Delay > time.Duration(float64(time.Millisecond)*iter.downloader.cfg.HighLatencyHubMs) { | |||||
| return consts.HubDistanceHighLatencyHub | |||||
| } | |||||
| return consts.StorageDistanceOther | |||||
| } | |||||
| func (iter *DownloadObjectIterator) downloadFromStorage(stg *stgmod.StorageDetail, req downloadReqeust2) (io.ReadCloser, error) { | |||||
| var strHandle *exec.DriverReadStream | |||||
| ft := ioswitch2.NewFromTo() | |||||
| toExec, handle := ioswitch2.NewToDriver(ioswitch2.RawStream()) | |||||
| toExec.Range = exec.Range{ | |||||
| Offset: req.Raw.Offset, | |||||
| } | |||||
| if req.Raw.Length != -1 { | |||||
| len := req.Raw.Length | |||||
| toExec.Range.Length = &len | |||||
| } | |||||
| ft.AddFrom(ioswitch2.NewFromShardstore(req.Detail.Object.FileHash, *stg.MasterHub, stg.Storage, ioswitch2.RawStream())).AddTo(toExec) | |||||
| strHandle = handle | |||||
| plans := exec.NewPlanBuilder() | |||||
| if err := parser.Parse(ft, plans); err != nil { | |||||
| return nil, fmt.Errorf("parsing plan: %w", err) | |||||
| } | |||||
| exeCtx := exec.NewExecContext() | |||||
| exec.SetValueByType(exeCtx, iter.downloader.stgMgr) | |||||
| exec := plans.Execute(exeCtx) | |||||
| go exec.Wait(context.TODO()) | |||||
| pw.Close() | |||||
| }() | |||||
| return exec.BeginRead(strHandle) | |||||
| return pr, nil | |||||
| } | } | ||||
| @@ -6,44 +6,30 @@ import ( | |||||
| "gitlink.org.cn/cloudream/common/pkgs/iterator" | "gitlink.org.cn/cloudream/common/pkgs/iterator" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/logger" | "gitlink.org.cn/cloudream/common/pkgs/logger" | ||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | |||||
| "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/math2" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/downloader/strategy" | |||||
| ) | ) | ||||
| func (iter *DownloadObjectIterator) downloadLRCObject(req downloadReqeust2, red *cdssdk.LRCRedundancy) (io.ReadCloser, error) { | |||||
| allStgs, err := iter.sortDownloadStorages(req) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| var blocks []downloadBlock | |||||
| selectedBlkIdx := make(map[int]bool) | |||||
| for _, stg := range allStgs { | |||||
| for _, b := range stg.Blocks { | |||||
| if b.Index >= red.M() || selectedBlkIdx[b.Index] { | |||||
| continue | |||||
| } | |||||
| blocks = append(blocks, downloadBlock{ | |||||
| Storage: stg.Storage, | |||||
| Block: b, | |||||
| }) | |||||
| selectedBlkIdx[b.Index] = true | |||||
| } | |||||
| } | |||||
| if len(blocks) < red.K { | |||||
| return nil, fmt.Errorf("not enough blocks to download lrc object") | |||||
| } | |||||
| var logStrs []any = []any{"downloading lrc object from blocks: "} | |||||
| for i, b := range blocks { | |||||
| func (iter *DownloadObjectIterator) downloadLRCReconstruct(req downloadReqeust2, strg strategy.LRCReconstructStrategy) (io.ReadCloser, error) { | |||||
| var logStrs []any = []any{fmt.Sprintf("downloading lrc object %v from: ", req.Raw.ObjectID)} | |||||
| for i, b := range strg.Blocks { | |||||
| if i > 0 { | if i > 0 { | ||||
| logStrs = append(logStrs, ", ") | logStrs = append(logStrs, ", ") | ||||
| } | } | ||||
| logStrs = append(logStrs, fmt.Sprintf("%v@%v", b.Block.Index, b.Storage.Storage.String())) | |||||
| logStrs = append(logStrs, fmt.Sprintf("%v@%v", b.Index, strg.Storages[i].Storage.String())) | |||||
| } | } | ||||
| logger.Debug(logStrs...) | logger.Debug(logStrs...) | ||||
| downloadBlks := make([]downloadBlock, len(strg.Blocks)) | |||||
| for i, b := range strg.Blocks { | |||||
| downloadBlks[i] = downloadBlock{ | |||||
| Block: b, | |||||
| Storage: strg.Storages[i], | |||||
| } | |||||
| } | |||||
| pr, pw := io.Pipe() | pr, pw := io.Pipe() | ||||
| go func() { | go func() { | ||||
| readPos := req.Raw.Offset | readPos := req.Raw.Offset | ||||
| @@ -52,8 +38,8 @@ func (iter *DownloadObjectIterator) downloadLRCObject(req downloadReqeust2, red | |||||
| totalReadLen = math2.Min(req.Raw.Length, totalReadLen) | totalReadLen = math2.Min(req.Raw.Length, totalReadLen) | ||||
| } | } | ||||
| firstStripIndex := readPos / int64(red.K) / int64(red.ChunkSize) | |||||
| stripIter := NewLRCStripIterator(iter.downloader, req.Detail.Object, blocks, red, firstStripIndex, iter.downloader.strips, iter.downloader.cfg.ECStripPrefetchCount) | |||||
| firstStripIndex := readPos / int64(strg.Redundancy.K) / int64(strg.Redundancy.ChunkSize) | |||||
| stripIter := NewLRCStripIterator(iter.downloader, req.Detail.Object, downloadBlks, strg.Redundancy, firstStripIndex, iter.downloader.strips, iter.downloader.cfg.ECStripPrefetchCount) | |||||
| defer stripIter.Close() | defer stripIter.Close() | ||||
| for totalReadLen > 0 { | for totalReadLen > 0 { | ||||
| @@ -68,7 +54,7 @@ func (iter *DownloadObjectIterator) downloadLRCObject(req downloadReqeust2, red | |||||
| } | } | ||||
| readRelativePos := readPos - strip.Position | readRelativePos := readPos - strip.Position | ||||
| nextStripPos := strip.Position + int64(red.K)*int64(red.ChunkSize) | |||||
| nextStripPos := strip.Position + int64(strg.Redundancy.K)*int64(strg.Redundancy.ChunkSize) | |||||
| curReadLen := math2.Min(totalReadLen, nextStripPos-readPos) | curReadLen := math2.Min(totalReadLen, nextStripPos-readPos) | ||||
| err = io2.WriteAll(pw, strip.Data[readRelativePos:readRelativePos+curReadLen]) | err = io2.WriteAll(pw, strip.Data[readRelativePos:readRelativePos+curReadLen]) | ||||
| @@ -9,6 +9,7 @@ import ( | |||||
| "gitlink.org.cn/cloudream/common/pkgs/iterator" | "gitlink.org.cn/cloudream/common/pkgs/iterator" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/logger" | "gitlink.org.cn/cloudream/common/pkgs/logger" | ||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | ||||
| "gitlink.org.cn/cloudream/common/utils/math2" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitchlrc" | "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitchlrc" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitchlrc/parser" | "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitchlrc/parser" | ||||
| ) | ) | ||||
| @@ -17,7 +18,7 @@ type LRCStripIterator struct { | |||||
| downloder *Downloader | downloder *Downloader | ||||
| object cdssdk.Object | object cdssdk.Object | ||||
| blocks []downloadBlock | blocks []downloadBlock | ||||
| red *cdssdk.LRCRedundancy | |||||
| red cdssdk.LRCRedundancy | |||||
| curStripIndex int64 | curStripIndex int64 | ||||
| cache *StripCache | cache *StripCache | ||||
| dataChan chan dataChanEntry | dataChan chan dataChanEntry | ||||
| @@ -26,7 +27,7 @@ type LRCStripIterator struct { | |||||
| inited bool | inited bool | ||||
| } | } | ||||
| func NewLRCStripIterator(downloder *Downloader, object cdssdk.Object, blocks []downloadBlock, red *cdssdk.LRCRedundancy, beginStripIndex int64, cache *StripCache, maxPrefetch int) *LRCStripIterator { | |||||
| func NewLRCStripIterator(downloder *Downloader, object cdssdk.Object, blocks []downloadBlock, red cdssdk.LRCRedundancy, beginStripIndex int64, cache *StripCache, maxPrefetch int) *LRCStripIterator { | |||||
| if maxPrefetch <= 0 { | if maxPrefetch <= 0 { | ||||
| maxPrefetch = 1 | maxPrefetch = 1 | ||||
| } | } | ||||
| @@ -101,7 +102,7 @@ func (s *LRCStripIterator) downloading() { | |||||
| froms = append(froms, ioswitchlrc.NewFromStorage(b.Block.FileHash, *stg.MasterHub, stg.Storage, b.Block.Index)) | froms = append(froms, ioswitchlrc.NewFromStorage(b.Block.FileHash, *stg.MasterHub, stg.Storage, b.Block.Index)) | ||||
| } | } | ||||
| toExec, hd := ioswitchlrc.NewToDriverWithRange(-1, exec.Range{ | |||||
| toExec, hd := ioswitchlrc.NewToDriverWithRange(-1, math2.Range{ | |||||
| Offset: s.curStripIndex * int64(s.red.ChunkSize*s.red.K), | Offset: s.curStripIndex * int64(s.red.ChunkSize*s.red.K), | ||||
| }) | }) | ||||
| @@ -113,7 +114,7 @@ func (s *LRCStripIterator) downloading() { | |||||
| } | } | ||||
| exeCtx := exec.NewExecContext() | exeCtx := exec.NewExecContext() | ||||
| exec.SetValueByType(exeCtx, s.downloder.stgMgr) | |||||
| exec.SetValueByType(exeCtx, s.downloder.stgAgts) | |||||
| exec := plans.Execute(exeCtx) | exec := plans.Execute(exeCtx) | ||||
| @@ -0,0 +1,6 @@ | |||||
| package strategy | |||||
| type Config struct { | |||||
| // 当到下载节点的延迟高于这个值时,该节点在评估时会有更高的分数惩罚,单位:ms | |||||
| HighLatencyHubMs float64 `json:"highLatencyHubMs"` | |||||
| } | |||||
| @@ -0,0 +1,337 @@ | |||||
| package strategy | |||||
| import ( | |||||
| "fmt" | |||||
| "math" | |||||
| "reflect" | |||||
| "time" | |||||
| "github.com/samber/lo" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/bitmap" | |||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | |||||
| "gitlink.org.cn/cloudream/common/utils/math2" | |||||
| "gitlink.org.cn/cloudream/common/utils/sort2" | |||||
| "gitlink.org.cn/cloudream/storage/common/consts" | |||||
| stgmod "gitlink.org.cn/cloudream/storage/common/models" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/metacache" | |||||
| ) | |||||
| type Request struct { | |||||
| Detail stgmod.ObjectDetail | |||||
| Range math2.Range | |||||
| DestHub cdssdk.HubID // 可以为0。此字段不为0时,DestLocation字段无意义。 | |||||
| DestLocation cdssdk.LocationID // 可以为0 | |||||
| } | |||||
| type Strategy interface { | |||||
| GetDetail() stgmod.ObjectDetail | |||||
| } | |||||
| // 直接下载完整对象 | |||||
| type DirectStrategy struct { | |||||
| Detail stgmod.ObjectDetail | |||||
| Storage stgmod.StorageDetail | |||||
| } | |||||
| func (s *DirectStrategy) GetDetail() stgmod.ObjectDetail { | |||||
| return s.Detail | |||||
| } | |||||
| // 从指定对象重建对象 | |||||
| type ECReconstructStrategy struct { | |||||
| Detail stgmod.ObjectDetail | |||||
| Redundancy cdssdk.ECRedundancy | |||||
| Blocks []stgmod.ObjectBlock | |||||
| Storages []stgmod.StorageDetail | |||||
| } | |||||
| func (s *ECReconstructStrategy) GetDetail() stgmod.ObjectDetail { | |||||
| return s.Detail | |||||
| } | |||||
| type LRCReconstructStrategy struct { | |||||
| Detail stgmod.ObjectDetail | |||||
| Redundancy cdssdk.LRCRedundancy | |||||
| Blocks []stgmod.ObjectBlock | |||||
| Storages []stgmod.StorageDetail | |||||
| } | |||||
| func (s *LRCReconstructStrategy) GetDetail() stgmod.ObjectDetail { | |||||
| return s.Detail | |||||
| } | |||||
| type Selector struct { | |||||
| cfg Config | |||||
| storageMeta *metacache.StorageMeta | |||||
| hubMeta *metacache.HubMeta | |||||
| connectivity *metacache.Connectivity | |||||
| } | |||||
| func NewSelector(cfg Config, storageMeta *metacache.StorageMeta, hubMeta *metacache.HubMeta, connectivity *metacache.Connectivity) *Selector { | |||||
| return &Selector{ | |||||
| cfg: cfg, | |||||
| storageMeta: storageMeta, | |||||
| hubMeta: hubMeta, | |||||
| connectivity: connectivity, | |||||
| } | |||||
| } | |||||
| func (s *Selector) Select(req Request) (Strategy, error) { | |||||
| req2 := request2{ | |||||
| Detail: req.Detail, | |||||
| Range: req.Range, | |||||
| DestLocation: req.DestLocation, | |||||
| } | |||||
| if req.DestHub != 0 { | |||||
| req2.DestHub = s.hubMeta.Get(req.DestHub) | |||||
| } | |||||
| switch red := req.Detail.Object.Redundancy.(type) { | |||||
| case *cdssdk.NoneRedundancy: | |||||
| return s.selectForNoneOrRep(req2) | |||||
| case *cdssdk.RepRedundancy: | |||||
| return s.selectForNoneOrRep(req2) | |||||
| case *cdssdk.ECRedundancy: | |||||
| return s.selectForEC(req2, *red) | |||||
| case *cdssdk.LRCRedundancy: | |||||
| return s.selectForLRC(req2, *red) | |||||
| } | |||||
| return nil, fmt.Errorf("unsupported redundancy type: %v of object %v", reflect.TypeOf(req.Detail.Object.Redundancy), req.Detail.Object.ObjectID) | |||||
| } | |||||
| type downloadStorageInfo struct { | |||||
| Storage stgmod.StorageDetail | |||||
| ObjectPinned bool | |||||
| Blocks []stgmod.ObjectBlock | |||||
| Distance float64 | |||||
| } | |||||
| type downloadBlock struct { | |||||
| Storage stgmod.StorageDetail | |||||
| Block stgmod.ObjectBlock | |||||
| } | |||||
| type request2 struct { | |||||
| Detail stgmod.ObjectDetail | |||||
| Range math2.Range | |||||
| DestHub *cdssdk.Hub | |||||
| DestLocation cdssdk.LocationID | |||||
| } | |||||
| func (s *Selector) selectForNoneOrRep(req request2) (Strategy, error) { | |||||
| sortedStgs := s.sortDownloadStorages(req) | |||||
| if len(sortedStgs) == 0 { | |||||
| return nil, fmt.Errorf("no storage available for download") | |||||
| } | |||||
| _, blks := s.getMinReadingBlockSolution(sortedStgs, 1) | |||||
| if len(blks) == 0 { | |||||
| return nil, fmt.Errorf("no block available for download") | |||||
| } | |||||
| return &DirectStrategy{ | |||||
| Detail: req.Detail, | |||||
| Storage: sortedStgs[0].Storage, | |||||
| }, nil | |||||
| } | |||||
| func (s *Selector) selectForEC(req request2, red cdssdk.ECRedundancy) (Strategy, error) { | |||||
| sortedStgs := s.sortDownloadStorages(req) | |||||
| if len(sortedStgs) == 0 { | |||||
| return nil, fmt.Errorf("no storage available for download") | |||||
| } | |||||
| bsc, blocks := s.getMinReadingBlockSolution(sortedStgs, red.K) | |||||
| osc, stg := s.getMinReadingObjectSolution(sortedStgs, red.K) | |||||
| if bsc < osc { | |||||
| bs := make([]stgmod.ObjectBlock, len(blocks)) | |||||
| ss := make([]stgmod.StorageDetail, len(blocks)) | |||||
| for i, b := range blocks { | |||||
| bs[i] = b.Block | |||||
| ss[i] = b.Storage | |||||
| } | |||||
| return &ECReconstructStrategy{ | |||||
| Detail: req.Detail, | |||||
| Redundancy: red, | |||||
| Blocks: bs, | |||||
| Storages: ss, | |||||
| }, nil | |||||
| } | |||||
| // bsc >= osc,如果osc是MaxFloat64,那么bsc也一定是,也就意味着没有足够块来恢复文件 | |||||
| if osc == math.MaxFloat64 { | |||||
| return nil, fmt.Errorf("no enough blocks to reconstruct the object %v , want %d, get only %d", req.Detail.Object.ObjectID, red.K, len(blocks)) | |||||
| } | |||||
| return &DirectStrategy{ | |||||
| Detail: req.Detail, | |||||
| Storage: stg, | |||||
| }, nil | |||||
| } | |||||
| func (s *Selector) selectForLRC(req request2, red cdssdk.LRCRedundancy) (Strategy, error) { | |||||
| sortedStgs := s.sortDownloadStorages(req) | |||||
| if len(sortedStgs) == 0 { | |||||
| return nil, fmt.Errorf("no storage available for download") | |||||
| } | |||||
| var blocks []downloadBlock | |||||
| selectedBlkIdx := make(map[int]bool) | |||||
| for _, stg := range sortedStgs { | |||||
| for _, b := range stg.Blocks { | |||||
| if b.Index >= red.M() || selectedBlkIdx[b.Index] { | |||||
| continue | |||||
| } | |||||
| blocks = append(blocks, downloadBlock{ | |||||
| Storage: stg.Storage, | |||||
| Block: b, | |||||
| }) | |||||
| selectedBlkIdx[b.Index] = true | |||||
| } | |||||
| } | |||||
| if len(blocks) < red.K { | |||||
| return nil, fmt.Errorf("not enough blocks to download lrc object") | |||||
| } | |||||
| bs := make([]stgmod.ObjectBlock, len(blocks)) | |||||
| ss := make([]stgmod.StorageDetail, len(blocks)) | |||||
| for i, b := range blocks { | |||||
| bs[i] = b.Block | |||||
| ss[i] = b.Storage | |||||
| } | |||||
| return &LRCReconstructStrategy{ | |||||
| Detail: req.Detail, | |||||
| Redundancy: red, | |||||
| Blocks: bs, | |||||
| Storages: ss, | |||||
| }, nil | |||||
| } | |||||
| func (s *Selector) sortDownloadStorages(req request2) []*downloadStorageInfo { | |||||
| var stgIDs []cdssdk.StorageID | |||||
| for _, id := range req.Detail.PinnedAt { | |||||
| if !lo.Contains(stgIDs, id) { | |||||
| stgIDs = append(stgIDs, id) | |||||
| } | |||||
| } | |||||
| for _, b := range req.Detail.Blocks { | |||||
| if !lo.Contains(stgIDs, b.StorageID) { | |||||
| stgIDs = append(stgIDs, b.StorageID) | |||||
| } | |||||
| } | |||||
| downloadStorageMap := make(map[cdssdk.StorageID]*downloadStorageInfo) | |||||
| for _, id := range req.Detail.PinnedAt { | |||||
| storage, ok := downloadStorageMap[id] | |||||
| if !ok { | |||||
| mod := s.storageMeta.Get(id) | |||||
| if mod == nil || mod.MasterHub == nil { | |||||
| continue | |||||
| } | |||||
| storage = &downloadStorageInfo{ | |||||
| Storage: *mod, | |||||
| ObjectPinned: true, | |||||
| Distance: s.getStorageDistance(req, *mod), | |||||
| } | |||||
| downloadStorageMap[id] = storage | |||||
| } | |||||
| storage.ObjectPinned = true | |||||
| } | |||||
| for _, b := range req.Detail.Blocks { | |||||
| storage, ok := downloadStorageMap[b.StorageID] | |||||
| if !ok { | |||||
| mod := s.storageMeta.Get(b.StorageID) | |||||
| if mod == nil || mod.MasterHub == nil { | |||||
| continue | |||||
| } | |||||
| storage = &downloadStorageInfo{ | |||||
| Storage: *mod, | |||||
| Distance: s.getStorageDistance(req, *mod), | |||||
| } | |||||
| downloadStorageMap[b.StorageID] = storage | |||||
| } | |||||
| storage.Blocks = append(storage.Blocks, b) | |||||
| } | |||||
| return sort2.Sort(lo.Values(downloadStorageMap), func(left, right *downloadStorageInfo) int { | |||||
| return sort2.Cmp(left.Distance, right.Distance) | |||||
| }) | |||||
| } | |||||
| func (s *Selector) getStorageDistance(req request2, src stgmod.StorageDetail) float64 { | |||||
| if req.DestHub != nil { | |||||
| if src.MasterHub.HubID == req.DestHub.HubID { | |||||
| return consts.StorageDistanceSameStorage | |||||
| } | |||||
| if src.MasterHub.LocationID == req.DestHub.LocationID { | |||||
| return consts.StorageDistanceSameLocation | |||||
| } | |||||
| latency := s.connectivity.Get(src.MasterHub.HubID, req.DestHub.HubID) | |||||
| if latency == nil || *latency > time.Duration(float64(time.Millisecond)*s.cfg.HighLatencyHubMs) { | |||||
| return consts.HubDistanceHighLatencyHub | |||||
| } | |||||
| return consts.StorageDistanceOther | |||||
| } | |||||
| if req.DestLocation != 0 { | |||||
| if src.MasterHub.LocationID == req.DestLocation { | |||||
| return consts.StorageDistanceSameLocation | |||||
| } | |||||
| } | |||||
| return consts.StorageDistanceOther | |||||
| } | |||||
| func (s *Selector) getMinReadingBlockSolution(sortedStgs []*downloadStorageInfo, k int) (float64, []downloadBlock) { | |||||
| gotBlocksMap := bitmap.Bitmap64(0) | |||||
| var gotBlocks []downloadBlock | |||||
| dist := float64(0.0) | |||||
| for _, n := range sortedStgs { | |||||
| for _, b := range n.Blocks { | |||||
| if !gotBlocksMap.Get(b.Index) { | |||||
| gotBlocks = append(gotBlocks, downloadBlock{ | |||||
| Storage: n.Storage, | |||||
| Block: b, | |||||
| }) | |||||
| gotBlocksMap.Set(b.Index, true) | |||||
| dist += n.Distance | |||||
| } | |||||
| if len(gotBlocks) >= k { | |||||
| return dist, gotBlocks | |||||
| } | |||||
| } | |||||
| } | |||||
| return math.MaxFloat64, gotBlocks | |||||
| } | |||||
| func (s *Selector) getMinReadingObjectSolution(sortedStgs []*downloadStorageInfo, k int) (float64, stgmod.StorageDetail) { | |||||
| dist := math.MaxFloat64 | |||||
| var downloadStg stgmod.StorageDetail | |||||
| for _, n := range sortedStgs { | |||||
| if n.ObjectPinned && float64(k)*n.Distance < dist { | |||||
| dist = float64(k) * n.Distance | |||||
| stg := n.Storage | |||||
| downloadStg = stg | |||||
| } | |||||
| } | |||||
| return dist, downloadStg | |||||
| } | |||||
| @@ -9,6 +9,7 @@ import ( | |||||
| "gitlink.org.cn/cloudream/common/pkgs/iterator" | "gitlink.org.cn/cloudream/common/pkgs/iterator" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/logger" | "gitlink.org.cn/cloudream/common/pkgs/logger" | ||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | ||||
| "gitlink.org.cn/cloudream/common/utils/math2" | |||||
| stgmod "gitlink.org.cn/cloudream/storage/common/models" | stgmod "gitlink.org.cn/cloudream/storage/common/models" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2" | "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/parser" | "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/parser" | ||||
| @@ -28,7 +29,7 @@ type StripIterator struct { | |||||
| downloader *Downloader | downloader *Downloader | ||||
| object cdssdk.Object | object cdssdk.Object | ||||
| blocks []downloadBlock | blocks []downloadBlock | ||||
| red *cdssdk.ECRedundancy | |||||
| red cdssdk.ECRedundancy | |||||
| curStripIndex int64 | curStripIndex int64 | ||||
| cache *StripCache | cache *StripCache | ||||
| dataChan chan dataChanEntry | dataChan chan dataChanEntry | ||||
| @@ -46,7 +47,7 @@ type dataChanEntry struct { | |||||
| Error error | Error error | ||||
| } | } | ||||
| func NewStripIterator(downloader *Downloader, object cdssdk.Object, blocks []downloadBlock, red *cdssdk.ECRedundancy, beginStripIndex int64, cache *StripCache, maxPrefetch int) *StripIterator { | |||||
| func NewStripIterator(downloader *Downloader, object cdssdk.Object, blocks []downloadBlock, red cdssdk.ECRedundancy, beginStripIndex int64, cache *StripCache, maxPrefetch int) *StripIterator { | |||||
| if maxPrefetch <= 0 { | if maxPrefetch <= 0 { | ||||
| maxPrefetch = 1 | maxPrefetch = 1 | ||||
| } | } | ||||
| @@ -199,13 +200,13 @@ func (s *StripIterator) readStrip(stripIndex int64, buf []byte) (int, error) { | |||||
| } | } | ||||
| ft := ioswitch2.NewFromTo() | ft := ioswitch2.NewFromTo() | ||||
| ft.ECParam = s.red | |||||
| ft.ECParam = &s.red | |||||
| for _, b := range s.blocks { | for _, b := range s.blocks { | ||||
| stg := b.Storage | stg := b.Storage | ||||
| ft.AddFrom(ioswitch2.NewFromShardstore(b.Block.FileHash, *stg.MasterHub, stg.Storage, ioswitch2.ECSrteam(b.Block.Index))) | |||||
| ft.AddFrom(ioswitch2.NewFromShardstore(b.Block.FileHash, *stg.MasterHub, stg, ioswitch2.ECStream(b.Block.Index))) | |||||
| } | } | ||||
| toExec, hd := ioswitch2.NewToDriverWithRange(ioswitch2.RawStream(), exec.Range{ | |||||
| toExec, hd := ioswitch2.NewToDriverWithRange(ioswitch2.RawStream(), math2.Range{ | |||||
| Offset: stripIndex * s.red.StripSize(), | Offset: stripIndex * s.red.StripSize(), | ||||
| }) | }) | ||||
| ft.AddTo(toExec) | ft.AddTo(toExec) | ||||
| @@ -217,7 +218,7 @@ func (s *StripIterator) readStrip(stripIndex int64, buf []byte) (int, error) { | |||||
| } | } | ||||
| exeCtx := exec.NewExecContext() | exeCtx := exec.NewExecContext() | ||||
| exec.SetValueByType(exeCtx, s.downloader.stgMgr) | |||||
| exec.SetValueByType(exeCtx, s.downloader.stgAgts) | |||||
| exec := plans.Execute(exeCtx) | exec := plans.Execute(exeCtx) | ||||
| ctx, cancel := context.WithCancel(context.Background()) | ctx, cancel := context.WithCancel(context.Background()) | ||||
| @@ -3,6 +3,7 @@ package ioswitch2 | |||||
| import ( | import ( | ||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | ||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | ||||
| "gitlink.org.cn/cloudream/common/utils/math2" | |||||
| stgmod "gitlink.org.cn/cloudream/storage/common/models" | stgmod "gitlink.org.cn/cloudream/storage/common/models" | ||||
| ) | ) | ||||
| @@ -14,7 +15,7 @@ type To interface { | |||||
| // To所需要的文件流的范围。具体含义与DataIndex有关系: | // To所需要的文件流的范围。具体含义与DataIndex有关系: | ||||
| // 如果DataIndex == -1,则表示在整个文件的范围。 | // 如果DataIndex == -1,则表示在整个文件的范围。 | ||||
| // 如果DataIndex >= 0,则表示在文件的某个分片的范围。 | // 如果DataIndex >= 0,则表示在文件的某个分片的范围。 | ||||
| GetRange() exec.Range | |||||
| GetRange() math2.Range | |||||
| GetStreamIndex() StreamIndex | GetStreamIndex() StreamIndex | ||||
| } | } | ||||
| @@ -38,7 +39,7 @@ func RawStream() StreamIndex { | |||||
| } | } | ||||
| } | } | ||||
| func ECSrteam(index int) StreamIndex { | |||||
| func ECStream(index int) StreamIndex { | |||||
| return StreamIndex{ | return StreamIndex{ | ||||
| Type: StreamIndexEC, | Type: StreamIndexEC, | ||||
| Index: index, | Index: index, | ||||
| @@ -96,7 +97,7 @@ type FromDriver struct { | |||||
| func NewFromDriver(strIdx StreamIndex) (*FromDriver, *exec.DriverWriteStream) { | func NewFromDriver(strIdx StreamIndex) (*FromDriver, *exec.DriverWriteStream) { | ||||
| handle := &exec.DriverWriteStream{ | handle := &exec.DriverWriteStream{ | ||||
| RangeHint: &exec.Range{}, | |||||
| RangeHint: &math2.Range{}, | |||||
| } | } | ||||
| return &FromDriver{ | return &FromDriver{ | ||||
| Handle: handle, | Handle: handle, | ||||
| @@ -111,11 +112,11 @@ func (f *FromDriver) GetStreamIndex() StreamIndex { | |||||
| type FromShardstore struct { | type FromShardstore struct { | ||||
| FileHash cdssdk.FileHash | FileHash cdssdk.FileHash | ||||
| Hub cdssdk.Hub | Hub cdssdk.Hub | ||||
| Storage cdssdk.Storage | |||||
| Storage stgmod.StorageDetail | |||||
| StreamIndex StreamIndex | StreamIndex StreamIndex | ||||
| } | } | ||||
| func NewFromShardstore(fileHash cdssdk.FileHash, hub cdssdk.Hub, storage cdssdk.Storage, strIdx StreamIndex) *FromShardstore { | |||||
| func NewFromShardstore(fileHash cdssdk.FileHash, hub cdssdk.Hub, storage stgmod.StorageDetail, strIdx StreamIndex) *FromShardstore { | |||||
| return &FromShardstore{ | return &FromShardstore{ | ||||
| FileHash: fileHash, | FileHash: fileHash, | ||||
| Hub: hub, | Hub: hub, | ||||
| @@ -131,7 +132,7 @@ func (f *FromShardstore) GetStreamIndex() StreamIndex { | |||||
| type ToDriver struct { | type ToDriver struct { | ||||
| Handle *exec.DriverReadStream | Handle *exec.DriverReadStream | ||||
| StreamIndex StreamIndex | StreamIndex StreamIndex | ||||
| Range exec.Range | |||||
| Range math2.Range | |||||
| } | } | ||||
| func NewToDriver(strIdx StreamIndex) (*ToDriver, *exec.DriverReadStream) { | func NewToDriver(strIdx StreamIndex) (*ToDriver, *exec.DriverReadStream) { | ||||
| @@ -142,7 +143,7 @@ func NewToDriver(strIdx StreamIndex) (*ToDriver, *exec.DriverReadStream) { | |||||
| }, &str | }, &str | ||||
| } | } | ||||
| func NewToDriverWithRange(strIdx StreamIndex, rng exec.Range) (*ToDriver, *exec.DriverReadStream) { | |||||
| func NewToDriverWithRange(strIdx StreamIndex, rng math2.Range) (*ToDriver, *exec.DriverReadStream) { | |||||
| str := exec.DriverReadStream{} | str := exec.DriverReadStream{} | ||||
| return &ToDriver{ | return &ToDriver{ | ||||
| Handle: &str, | Handle: &str, | ||||
| @@ -155,7 +156,7 @@ func (t *ToDriver) GetStreamIndex() StreamIndex { | |||||
| return t.StreamIndex | return t.StreamIndex | ||||
| } | } | ||||
| func (t *ToDriver) GetRange() exec.Range { | |||||
| func (t *ToDriver) GetRange() math2.Range { | |||||
| return t.Range | return t.Range | ||||
| } | } | ||||
| @@ -163,7 +164,7 @@ type ToShardStore struct { | |||||
| Hub cdssdk.Hub | Hub cdssdk.Hub | ||||
| Storage stgmod.StorageDetail | Storage stgmod.StorageDetail | ||||
| StreamIndex StreamIndex | StreamIndex StreamIndex | ||||
| Range exec.Range | |||||
| Range math2.Range | |||||
| FileHashStoreKey string | FileHashStoreKey string | ||||
| } | } | ||||
| @@ -176,7 +177,7 @@ func NewToShardStore(hub cdssdk.Hub, stg stgmod.StorageDetail, strIdx StreamInde | |||||
| } | } | ||||
| } | } | ||||
| func NewToShardStoreWithRange(hub cdssdk.Hub, stg stgmod.StorageDetail, streamIndex StreamIndex, fileHashStoreKey string, rng exec.Range) *ToShardStore { | |||||
| func NewToShardStoreWithRange(hub cdssdk.Hub, stg stgmod.StorageDetail, streamIndex StreamIndex, fileHashStoreKey string, rng math2.Range) *ToShardStore { | |||||
| return &ToShardStore{ | return &ToShardStore{ | ||||
| Hub: hub, | Hub: hub, | ||||
| Storage: stg, | Storage: stg, | ||||
| @@ -190,25 +191,21 @@ func (t *ToShardStore) GetStreamIndex() StreamIndex { | |||||
| return t.StreamIndex | return t.StreamIndex | ||||
| } | } | ||||
| func (t *ToShardStore) GetRange() exec.Range { | |||||
| func (t *ToShardStore) GetRange() math2.Range { | |||||
| return t.Range | return t.Range | ||||
| } | } | ||||
| type LoadToShared struct { | type LoadToShared struct { | ||||
| Hub cdssdk.Hub | |||||
| Storage cdssdk.Storage | |||||
| UserID cdssdk.UserID | |||||
| PackageID cdssdk.PackageID | |||||
| Path string | |||||
| Hub cdssdk.Hub | |||||
| Storage stgmod.StorageDetail | |||||
| ObjectPath string | |||||
| } | } | ||||
| func NewLoadToShared(hub cdssdk.Hub, storage cdssdk.Storage, userID cdssdk.UserID, packageID cdssdk.PackageID, path string) *LoadToShared { | |||||
| func NewLoadToShared(hub cdssdk.Hub, storage stgmod.StorageDetail, objectPath string) *LoadToShared { | |||||
| return &LoadToShared{ | return &LoadToShared{ | ||||
| Hub: hub, | |||||
| Storage: storage, | |||||
| UserID: userID, | |||||
| PackageID: packageID, | |||||
| Path: path, | |||||
| Hub: hub, | |||||
| Storage: storage, | |||||
| ObjectPath: objectPath, | |||||
| } | } | ||||
| } | } | ||||
| @@ -218,6 +215,6 @@ func (t *LoadToShared) GetStreamIndex() StreamIndex { | |||||
| } | } | ||||
| } | } | ||||
| func (t *LoadToShared) GetRange() exec.Range { | |||||
| return exec.Range{} | |||||
| func (t *LoadToShared) GetRange() math2.Range { | |||||
| return math2.Range{} | |||||
| } | } | ||||
| @@ -6,7 +6,7 @@ import ( | |||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/dag" | "gitlink.org.cn/cloudream/common/pkgs/ioswitch/dag" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | ||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/svcmgr" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/agtpool" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/types" | "gitlink.org.cn/cloudream/storage/common/pkgs/storage/types" | ||||
| ) | ) | ||||
| @@ -14,6 +14,9 @@ func init() { | |||||
| exec.UseOp[*BypassToShardStore]() | exec.UseOp[*BypassToShardStore]() | ||||
| exec.UseVarValue[*BypassFileInfoValue]() | exec.UseVarValue[*BypassFileInfoValue]() | ||||
| exec.UseVarValue[*BypassHandleResultValue]() | exec.UseVarValue[*BypassHandleResultValue]() | ||||
| exec.UseOp[*BypassFromShardStore]() | |||||
| exec.UseVarValue[*BypassFilePathValue]() | |||||
| } | } | ||||
| type BypassFileInfoValue struct { | type BypassFileInfoValue struct { | ||||
| @@ -44,19 +47,19 @@ type BypassToShardStore struct { | |||||
| } | } | ||||
| func (o *BypassToShardStore) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | func (o *BypassToShardStore) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | ||||
| svcMgr, err := exec.GetValueByType[*svcmgr.Manager](ctx) | |||||
| stgAgts, err := exec.GetValueByType[*agtpool.AgentPool](ctx) | |||||
| if err != nil { | if err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| shardStore, err := svcMgr.GetShardStore(o.StorageID) | |||||
| shardStore, err := stgAgts.GetShardStore(o.StorageID) | |||||
| if err != nil { | if err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| notifier, ok := shardStore.(types.BypassNotifier) | |||||
| br, ok := shardStore.(types.BypassWrite) | |||||
| if !ok { | if !ok { | ||||
| return fmt.Errorf("shard store %v not support bypass", o.StorageID) | |||||
| return fmt.Errorf("shard store %v not support bypass write", o.StorageID) | |||||
| } | } | ||||
| fileInfo, err := exec.BindVar[*BypassFileInfoValue](e, ctx.Context, o.BypassFileInfo) | fileInfo, err := exec.BindVar[*BypassFileInfoValue](e, ctx.Context, o.BypassFileInfo) | ||||
| @@ -64,7 +67,7 @@ func (o *BypassToShardStore) Execute(ctx *exec.ExecContext, e *exec.Executor) er | |||||
| return err | return err | ||||
| } | } | ||||
| err = notifier.BypassUploaded(fileInfo.BypassFileInfo) | |||||
| err = br.BypassUploaded(fileInfo.BypassFileInfo) | |||||
| if err != nil { | if err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| @@ -78,6 +81,52 @@ func (o *BypassToShardStore) String() string { | |||||
| return fmt.Sprintf("BypassToShardStore[StorageID:%v] Info: %v, Callback: %v", o.StorageID, o.BypassFileInfo, o.BypassCallback) | return fmt.Sprintf("BypassToShardStore[StorageID:%v] Info: %v, Callback: %v", o.StorageID, o.BypassFileInfo, o.BypassCallback) | ||||
| } | } | ||||
| type BypassFilePathValue struct { | |||||
| types.BypassFilePath | |||||
| } | |||||
| func (v *BypassFilePathValue) Clone() exec.VarValue { | |||||
| return &BypassFilePathValue{ | |||||
| BypassFilePath: v.BypassFilePath, | |||||
| } | |||||
| } | |||||
| type BypassFromShardStore struct { | |||||
| StorageID cdssdk.StorageID | |||||
| FileHash cdssdk.FileHash | |||||
| Output exec.VarID | |||||
| } | |||||
| func (o *BypassFromShardStore) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| stgAgts, err := exec.GetValueByType[*agtpool.AgentPool](ctx) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| shardStore, err := stgAgts.GetShardStore(o.StorageID) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| br, ok := shardStore.(types.BypassRead) | |||||
| if !ok { | |||||
| return fmt.Errorf("shard store %v not support bypass read", o.StorageID) | |||||
| } | |||||
| path, err := br.BypassRead(o.FileHash) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| e.PutVar(o.Output, &BypassFilePathValue{BypassFilePath: path}) | |||||
| return nil | |||||
| } | |||||
| func (o *BypassFromShardStore) String() string { | |||||
| return fmt.Sprintf("BypassFromShardStore[StorageID:%v] FileHash: %v, Output: %v", o.StorageID, o.FileHash, o.Output) | |||||
| } | |||||
| // 旁路写入 | |||||
| type BypassToShardStoreNode struct { | type BypassToShardStoreNode struct { | ||||
| dag.NodeBase | dag.NodeBase | ||||
| StorageID cdssdk.StorageID | StorageID cdssdk.StorageID | ||||
| @@ -103,19 +152,58 @@ func (n *BypassToShardStoreNode) BypassFileInfoSlot() dag.ValueInputSlot { | |||||
| } | } | ||||
| } | } | ||||
| func (n *BypassToShardStoreNode) BypassCallbackVar() *dag.ValueVar { | |||||
| return n.OutputValues().Get(0) | |||||
| func (n *BypassToShardStoreNode) BypassCallbackVar() dag.ValueOutputSlot { | |||||
| return dag.ValueOutputSlot{ | |||||
| Node: n, | |||||
| Index: 0, | |||||
| } | |||||
| } | } | ||||
| func (n *BypassToShardStoreNode) FileHashVar() *dag.ValueVar { | |||||
| return n.OutputValues().Get(1) | |||||
| func (n *BypassToShardStoreNode) FileHashVar() dag.ValueOutputSlot { | |||||
| return dag.ValueOutputSlot{ | |||||
| Node: n, | |||||
| Index: 1, | |||||
| } | |||||
| } | } | ||||
| func (t *BypassToShardStoreNode) GenerateOp() (exec.Op, error) { | func (t *BypassToShardStoreNode) GenerateOp() (exec.Op, error) { | ||||
| return &BypassToShardStore{ | return &BypassToShardStore{ | ||||
| StorageID: t.StorageID, | StorageID: t.StorageID, | ||||
| BypassFileInfo: t.BypassFileInfoSlot().Var().VarID, | BypassFileInfo: t.BypassFileInfoSlot().Var().VarID, | ||||
| BypassCallback: t.BypassCallbackVar().VarID, | |||||
| FileHash: t.FileHashVar().VarID, | |||||
| BypassCallback: t.BypassCallbackVar().Var().VarID, | |||||
| FileHash: t.FileHashVar().Var().VarID, | |||||
| }, nil | |||||
| } | |||||
| // 旁路读取 | |||||
| type BypassFromShardStoreNode struct { | |||||
| dag.NodeBase | |||||
| StorageID cdssdk.StorageID | |||||
| FileHash cdssdk.FileHash | |||||
| } | |||||
| func (b *GraphNodeBuilder) NewBypassFromShardStore(storageID cdssdk.StorageID, fileHash cdssdk.FileHash) *BypassFromShardStoreNode { | |||||
| node := &BypassFromShardStoreNode{ | |||||
| StorageID: storageID, | |||||
| FileHash: fileHash, | |||||
| } | |||||
| b.AddNode(node) | |||||
| node.OutputValues().Init(node, 1) | |||||
| return node | |||||
| } | |||||
| func (n *BypassFromShardStoreNode) FilePathVar() dag.ValueOutputSlot { | |||||
| return dag.ValueOutputSlot{ | |||||
| Node: n, | |||||
| Index: 0, | |||||
| } | |||||
| } | |||||
| func (n *BypassFromShardStoreNode) GenerateOp() (exec.Op, error) { | |||||
| return &BypassFromShardStore{ | |||||
| StorageID: n.StorageID, | |||||
| FileHash: n.FileHash, | |||||
| Output: n.FilePathVar().Var().VarID, | |||||
| }, nil | }, nil | ||||
| } | } | ||||
| @@ -37,7 +37,10 @@ func (o *ChunkedSplit) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| sem := semaphore.NewWeighted(int64(len(outputs))) | sem := semaphore.NewWeighted(int64(len(outputs))) | ||||
| for i := range outputs { | for i := range outputs { | ||||
| sem.Acquire(ctx.Context, 1) | |||||
| err = sem.Acquire(ctx.Context, 1) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| e.PutVar(o.Outputs[i], &exec.StreamValue{ | e.PutVar(o.Outputs[i], &exec.StreamValue{ | ||||
| Stream: io2.AfterReadClosedOnce(outputs[i], func(closer io.ReadCloser) { | Stream: io2.AfterReadClosedOnce(outputs[i], func(closer io.ReadCloser) { | ||||
| @@ -3,6 +3,7 @@ package ops2 | |||||
| import ( | import ( | ||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/dag" | "gitlink.org.cn/cloudream/common/pkgs/ioswitch/dag" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | ||||
| "gitlink.org.cn/cloudream/common/utils/math2" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2" | "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2" | ||||
| ) | ) | ||||
| @@ -45,7 +46,7 @@ type ToDriverNode struct { | |||||
| dag.NodeBase | dag.NodeBase | ||||
| To ioswitch2.To | To ioswitch2.To | ||||
| Handle *exec.DriverReadStream | Handle *exec.DriverReadStream | ||||
| Range exec.Range | |||||
| Range math2.Range | |||||
| } | } | ||||
| func (b *GraphNodeBuilder) NewToDriver(to ioswitch2.To, handle *exec.DriverReadStream) *ToDriverNode { | func (b *GraphNodeBuilder) NewToDriver(to ioswitch2.To, handle *exec.DriverReadStream) *ToDriverNode { | ||||
| @@ -10,6 +10,7 @@ import ( | |||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/utils" | "gitlink.org.cn/cloudream/common/pkgs/ioswitch/utils" | ||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | ||||
| "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" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ec" | "gitlink.org.cn/cloudream/storage/common/pkgs/ec" | ||||
| ) | ) | ||||
| @@ -45,20 +46,35 @@ func (o *ECMultiply) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| outputWrs[i] = wr | outputWrs[i] = wr | ||||
| } | } | ||||
| fut := future.NewSetVoid() | |||||
| go func() { | |||||
| mul := ec.GaloisMultiplier().BuildGalois() | |||||
| inputChunks := make([][]byte, len(o.Inputs)) | |||||
| for i := range o.Inputs { | |||||
| inputChunks[i] = make([]byte, math2.Min(o.ChunkSize, 64*1024)) | |||||
| } | |||||
| inputChunks := make([][]byte, len(o.Inputs)) | |||||
| for i := range o.Inputs { | |||||
| inputChunks[i] = make([]byte, o.ChunkSize) | |||||
| } | |||||
| // 输出用两个缓冲轮换 | |||||
| outputBufPool := sync2.NewBucketPool[[][]byte]() | |||||
| 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, o.ChunkSize) | |||||
| outputChunks[i] = make([]byte, math2.Min(o.ChunkSize, 64*1024)) | |||||
| } | } | ||||
| outputBufPool.PutEmpty(outputChunks) | |||||
| } | |||||
| fut := future.NewSetVoid() | |||||
| go func() { | |||||
| mul := ec.GaloisMultiplier().BuildGalois() | |||||
| defer outputBufPool.WakeUpAll() | |||||
| 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 | ||||
| @@ -72,12 +88,34 @@ func (o *ECMultiply) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| return | return | ||||
| } | } | ||||
| err = mul.Multiply(o.Coef, inputChunks, outputChunks) | |||||
| outputBuf, ok := outputBufPool.GetEmpty() | |||||
| if !ok { | |||||
| return | |||||
| } | |||||
| for i := range outputBuf { | |||||
| outputBuf[i] = outputBuf[i][:curReadLen] | |||||
| } | |||||
| err = mul.Multiply(o.Coef, inputChunks, outputBuf) | |||||
| if err != nil { | if err != nil { | ||||
| fut.SetError(err) | fut.SetError(err) | ||||
| return | return | ||||
| } | } | ||||
| outputBufPool.PutFilled(outputBuf) | |||||
| readLenIdx = (readLenIdx + 1) % len(readLens) | |||||
| } | |||||
| }() | |||||
| go func() { | |||||
| defer outputBufPool.WakeUpAll() | |||||
| for { | |||||
| outputChunks, ok := outputBufPool.GetFilled() | |||||
| if !ok { | |||||
| return | |||||
| } | |||||
| for i := range o.Outputs { | for i := range o.Outputs { | ||||
| err := io2.WriteAll(outputWrs[i], outputChunks[i]) | err := io2.WriteAll(outputWrs[i], outputChunks[i]) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -85,6 +123,8 @@ func (o *ECMultiply) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| return | return | ||||
| } | } | ||||
| } | } | ||||
| outputBufPool.PutEmpty(outputChunks) | |||||
| } | } | ||||
| }() | }() | ||||
| @@ -1,12 +1,13 @@ | |||||
| package ops2 | package ops2 | ||||
| /* | |||||
| import ( | import ( | ||||
| "fmt" | "fmt" | ||||
| "github.com/samber/lo" | "github.com/samber/lo" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | ||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/svcmgr" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/agtpool" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/types" | "gitlink.org.cn/cloudream/storage/common/pkgs/storage/types" | ||||
| ) | ) | ||||
| @@ -19,17 +20,17 @@ type InternalFaaSGalMultiply struct { | |||||
| } | } | ||||
| func (o *InternalFaaSGalMultiply) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | func (o *InternalFaaSGalMultiply) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | ||||
| stgMgr, err := exec.GetValueByType[*svcmgr.Manager](ctx) | |||||
| stgAgts, err := exec.GetValueByType[*agtpool.AgentPool](ctx) | |||||
| if err != nil { | if err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| fass, err := svcmgr.GetComponent[types.InternalFaaSCall](stgMgr, o.StorageID) | |||||
| fass, err := agtpool.GetComponent[types.InternalFaaSCall](stgAgts, o.StorageID) | |||||
| if err != nil { | if err != nil { | ||||
| return fmt.Errorf("getting faas component: %w", err) | return fmt.Errorf("getting faas component: %w", err) | ||||
| } | } | ||||
| tmp, err := svcmgr.GetComponent[types.TempStore](stgMgr, o.StorageID) | |||||
| tmp, err := agtpool.GetComponent[types.TempStore](stgAgts, o.StorageID) | |||||
| if err != nil { | if err != nil { | ||||
| return fmt.Errorf("getting temp store component: %w", err) | return fmt.Errorf("getting temp store component: %w", err) | ||||
| } | } | ||||
| @@ -58,3 +59,4 @@ func (o *InternalFaaSGalMultiply) Execute(ctx *exec.ExecContext, e *exec.Executo | |||||
| exec.PutArray(e, o.OutputFilePathes, outputVars) | exec.PutArray(e, o.OutputFilePathes, outputVars) | ||||
| return nil | return nil | ||||
| } | } | ||||
| */ | |||||
| @@ -48,20 +48,22 @@ type MultipartInitiator struct { | |||||
| } | } | ||||
| func (o *MultipartInitiator) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | func (o *MultipartInitiator) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | ||||
| initiator, err := factory.CreateComponent[types.MultipartInitiator](o.Storage) | |||||
| blder := factory.GetBuilder(o.Storage) | |||||
| multi, err := blder.CreateMultiparter() | |||||
| if err != nil { | if err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| defer initiator.Abort() | |||||
| // 启动一个新的上传任务 | |||||
| initState, err := initiator.Initiate(ctx.Context) | |||||
| // 启动一个新的上传任务W | |||||
| multiTask, err := multi.Initiate(ctx.Context) | |||||
| if err != nil { | if err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| defer multiTask.Abort() | |||||
| // 分发上传参数 | // 分发上传参数 | ||||
| e.PutVar(o.UploadArgs, &MultipartUploadArgsValue{ | e.PutVar(o.UploadArgs, &MultipartUploadArgsValue{ | ||||
| InitState: initState, | |||||
| InitState: multiTask.InitState(), | |||||
| }) | }) | ||||
| // 收集分片上传结果 | // 收集分片上传结果 | ||||
| @@ -76,7 +78,7 @@ func (o *MultipartInitiator) Execute(ctx *exec.ExecContext, e *exec.Executor) er | |||||
| } | } | ||||
| // 合并分片 | // 合并分片 | ||||
| fileInfo, err := initiator.JoinParts(ctx.Context, partInfos) | |||||
| fileInfo, err := multiTask.JoinParts(ctx.Context, partInfos) | |||||
| if err != nil { | if err != nil { | ||||
| return fmt.Errorf("completing multipart upload: %v", err) | return fmt.Errorf("completing multipart upload: %v", err) | ||||
| } | } | ||||
| @@ -93,7 +95,7 @@ func (o *MultipartInitiator) Execute(ctx *exec.ExecContext, e *exec.Executor) er | |||||
| } | } | ||||
| if cb.Commited { | if cb.Commited { | ||||
| initiator.Complete() | |||||
| multiTask.Complete() | |||||
| } | } | ||||
| return nil | return nil | ||||
| @@ -113,6 +115,7 @@ type MultipartUpload struct { | |||||
| } | } | ||||
| func (o *MultipartUpload) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | func (o *MultipartUpload) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | ||||
| blder := factory.GetBuilder(o.Storage) | |||||
| uploadArgs, err := exec.BindVar[*MultipartUploadArgsValue](e, ctx.Context, o.UploadArgs) | uploadArgs, err := exec.BindVar[*MultipartUploadArgsValue](e, ctx.Context, o.UploadArgs) | ||||
| if err != nil { | if err != nil { | ||||
| return err | return err | ||||
| @@ -124,13 +127,13 @@ func (o *MultipartUpload) Execute(ctx *exec.ExecContext, e *exec.Executor) error | |||||
| } | } | ||||
| defer partStr.Stream.Close() | defer partStr.Stream.Close() | ||||
| uploader, err := factory.CreateComponent[types.MultipartUploader](o.Storage) | |||||
| multi, err := blder.CreateMultiparter() | |||||
| if err != nil { | if err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| startTime := time.Now() | startTime := time.Now() | ||||
| uploadedInfo, err := uploader.UploadPart(ctx.Context, uploadArgs.InitState, o.PartSize, o.PartNumber, partStr.Stream) | |||||
| uploadedInfo, err := multi.UploadPart(ctx.Context, uploadArgs.InitState, o.PartSize, o.PartNumber, partStr.Stream) | |||||
| if err != nil { | if err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| @@ -163,12 +166,18 @@ func (b *GraphNodeBuilder) NewMultipartInitiator(storage stgmod.StorageDetail) * | |||||
| return node | return node | ||||
| } | } | ||||
| func (n *MultipartInitiatorNode) UploadArgsVar() *dag.ValueVar { | |||||
| return n.OutputValues().Get(0) | |||||
| func (n *MultipartInitiatorNode) UploadArgsVar() dag.ValueOutputSlot { | |||||
| return dag.ValueOutputSlot{ | |||||
| Node: n, | |||||
| Index: 0, | |||||
| } | |||||
| } | } | ||||
| func (n *MultipartInitiatorNode) BypassFileInfoVar() *dag.ValueVar { | |||||
| return n.OutputValues().Get(1) | |||||
| func (n *MultipartInitiatorNode) BypassFileInfoVar() dag.ValueOutputSlot { | |||||
| return dag.ValueOutputSlot{ | |||||
| Node: n, | |||||
| Index: 1, | |||||
| } | |||||
| } | } | ||||
| func (n *MultipartInitiatorNode) BypassCallbackSlot() dag.ValueInputSlot { | func (n *MultipartInitiatorNode) BypassCallbackSlot() dag.ValueInputSlot { | ||||
| @@ -188,9 +197,9 @@ func (n *MultipartInitiatorNode) AppendPartInfoSlot() dag.ValueInputSlot { | |||||
| func (n *MultipartInitiatorNode) GenerateOp() (exec.Op, error) { | func (n *MultipartInitiatorNode) GenerateOp() (exec.Op, error) { | ||||
| return &MultipartInitiator{ | return &MultipartInitiator{ | ||||
| Storage: n.Storage, | Storage: n.Storage, | ||||
| UploadArgs: n.UploadArgsVar().VarID, | |||||
| UploadArgs: n.UploadArgsVar().Var().VarID, | |||||
| UploadedParts: n.InputValues().GetVarIDsStart(1), | UploadedParts: n.InputValues().GetVarIDsStart(1), | ||||
| BypassFileOutput: n.BypassFileInfoVar().VarID, | |||||
| BypassFileOutput: n.BypassFileInfoVar().Var().VarID, | |||||
| BypassCallback: n.BypassCallbackSlot().Var().VarID, | BypassCallback: n.BypassCallbackSlot().Var().VarID, | ||||
| }, nil | }, nil | ||||
| } | } | ||||
| @@ -223,8 +232,11 @@ func (n *MultipartUploadNode) UploadArgsSlot() dag.ValueInputSlot { | |||||
| } | } | ||||
| } | } | ||||
| func (n *MultipartUploadNode) UploadResultVar() *dag.ValueVar { | |||||
| return n.OutputValues().Get(0) | |||||
| func (n *MultipartUploadNode) UploadResultVar() dag.ValueOutputSlot { | |||||
| return dag.ValueOutputSlot{ | |||||
| Node: n, | |||||
| Index: 0, | |||||
| } | |||||
| } | } | ||||
| func (n *MultipartUploadNode) PartStreamSlot() dag.StreamInputSlot { | func (n *MultipartUploadNode) PartStreamSlot() dag.StreamInputSlot { | ||||
| @@ -238,7 +250,7 @@ func (n *MultipartUploadNode) GenerateOp() (exec.Op, error) { | |||||
| return &MultipartUpload{ | return &MultipartUpload{ | ||||
| Storage: n.Storage, | Storage: n.Storage, | ||||
| UploadArgs: n.UploadArgsSlot().Var().VarID, | UploadArgs: n.UploadArgsSlot().Var().VarID, | ||||
| UploadResult: n.UploadResultVar().VarID, | |||||
| UploadResult: n.UploadResultVar().Var().VarID, | |||||
| PartStream: n.PartStreamSlot().Var().VarID, | PartStream: n.PartStreamSlot().Var().VarID, | ||||
| PartNumber: n.PartNumber, | PartNumber: n.PartNumber, | ||||
| PartSize: n.PartSize, | PartSize: n.PartSize, | ||||
| @@ -26,71 +26,3 @@ type ToNode interface { | |||||
| Input() dag.StreamInputSlot | Input() dag.StreamInputSlot | ||||
| SetInput(input *dag.StreamVar) | SetInput(input *dag.StreamVar) | ||||
| } | } | ||||
| // func formatStreamIO(node *dag.Node) string { | |||||
| // is := "" | |||||
| // for i, in := range node.InputStreams { | |||||
| // if i > 0 { | |||||
| // is += "," | |||||
| // } | |||||
| // if in == nil { | |||||
| // is += "." | |||||
| // } else { | |||||
| // is += fmt.Sprintf("%v", in.ID) | |||||
| // } | |||||
| // } | |||||
| // os := "" | |||||
| // for i, out := range node.OutputStreams { | |||||
| // if i > 0 | |||||
| // os += "," | |||||
| // } | |||||
| // if out == nil { | |||||
| // os += "." | |||||
| // } else { | |||||
| // os += fmt.Sprintf("%v", out.ID) | |||||
| // } | |||||
| // } | |||||
| // if is == "" && os == "" { | |||||
| // return "" | |||||
| // } | |||||
| // return fmt.Sprintf("S{%s>%s}", is, os) | |||||
| // } | |||||
| // func formatValueIO(node *dag.Node) string { | |||||
| // is := "" | |||||
| // for i, in := range node.InputValues { | |||||
| // if i > 0 { | |||||
| // is += "," | |||||
| // } | |||||
| // if in == nil { | |||||
| // is += "." | |||||
| // } else { | |||||
| // is += fmt.Sprintf("%v", in.ID) | |||||
| // } | |||||
| // } | |||||
| // os := "" | |||||
| // for i, out := range node.OutputValues { | |||||
| // if i > 0 { | |||||
| // os += "," | |||||
| // } | |||||
| // if out == nil { | |||||
| // os += "." | |||||
| // } else { | |||||
| // os += fmt.Sprintf("%v", out.ID) | |||||
| // } | |||||
| // } | |||||
| // if is == "" && os == "" { | |||||
| // return "" | |||||
| // } | |||||
| // return fmt.Sprintf("V{%s>%s}", is, os) | |||||
| // } | |||||
| @@ -81,7 +81,7 @@ func (o *Range) String() string { | |||||
| type RangeNode struct { | type RangeNode struct { | ||||
| dag.NodeBase | dag.NodeBase | ||||
| Range exec.Range | |||||
| Range math2.Range | |||||
| } | } | ||||
| func (b *GraphNodeBuilder) NewRange() *RangeNode { | func (b *GraphNodeBuilder) NewRange() *RangeNode { | ||||
| @@ -93,7 +93,7 @@ func (b *GraphNodeBuilder) NewRange() *RangeNode { | |||||
| return node | return node | ||||
| } | } | ||||
| func (t *RangeNode) RangeStream(input *dag.StreamVar, rng exec.Range) *dag.StreamVar { | |||||
| func (t *RangeNode) RangeStream(input *dag.StreamVar, rng math2.Range) *dag.StreamVar { | |||||
| input.To(t, 0) | input.To(t, 0) | ||||
| t.Range = rng | t.Range = rng | ||||
| return t.OutputStreams().Get(0) | return t.OutputStreams().Get(0) | ||||
| @@ -0,0 +1,115 @@ | |||||
| package ops2 | |||||
| import ( | |||||
| "fmt" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/dag" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | |||||
| stgmod "gitlink.org.cn/cloudream/storage/common/models" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/factory" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/types" | |||||
| ) | |||||
| func init() { | |||||
| exec.UseOp[*S2STransfer]() | |||||
| } | |||||
| type S2STransfer struct { | |||||
| Src stgmod.StorageDetail | |||||
| SrcPath exec.VarID | |||||
| Dst stgmod.StorageDetail | |||||
| Output exec.VarID | |||||
| BypassCallback exec.VarID | |||||
| } | |||||
| func (o *S2STransfer) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| srcPath, err := exec.BindVar[*BypassFilePathValue](e, ctx.Context, o.SrcPath) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| s2s, err := factory.GetBuilder(o.Dst).CreateS2STransfer() | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| // 传输文件 | |||||
| dstPath, err := s2s.Transfer(ctx.Context, o.Src, srcPath.Path) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| defer s2s.Abort() | |||||
| // 告知后续Op处理临时文件 | |||||
| e.PutVar(o.Output, &BypassFileInfoValue{BypassFileInfo: types.BypassFileInfo{ | |||||
| TempFilePath: dstPath, | |||||
| FileHash: srcPath.Info.Hash, | |||||
| Size: srcPath.Info.Size, | |||||
| }}) | |||||
| // 等待后续Op处理临时文件 | |||||
| cb, err := exec.BindVar[*BypassHandleResultValue](e, ctx.Context, o.BypassCallback) | |||||
| if err != nil { | |||||
| return fmt.Errorf("getting temp file callback: %v", err) | |||||
| } | |||||
| if cb.Commited { | |||||
| s2s.Complete() | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func (o *S2STransfer) String() string { | |||||
| return fmt.Sprintf("S2STransfer %v:%v -> %v:%v", o.Src.Storage.String(), o.SrcPath, o.Dst.Storage.String(), o.Output) | |||||
| } | |||||
| type S2STransferNode struct { | |||||
| dag.NodeBase | |||||
| Src stgmod.StorageDetail | |||||
| Dst stgmod.StorageDetail | |||||
| } | |||||
| func (b *GraphNodeBuilder) NewS2STransfer(src stgmod.StorageDetail, dst stgmod.StorageDetail) *S2STransferNode { | |||||
| n := &S2STransferNode{ | |||||
| Src: src, | |||||
| Dst: dst, | |||||
| } | |||||
| b.AddNode(n) | |||||
| n.OutputValues().Init(n, 1) | |||||
| n.InputValues().Init(2) | |||||
| return n | |||||
| } | |||||
| func (n *S2STransferNode) SrcPathSlot() dag.ValueInputSlot { | |||||
| return dag.ValueInputSlot{ | |||||
| Node: n, | |||||
| Index: 0, | |||||
| } | |||||
| } | |||||
| func (n *S2STransferNode) BypassCallbackSlot() dag.ValueInputSlot { | |||||
| return dag.ValueInputSlot{ | |||||
| Node: n, | |||||
| Index: 1, | |||||
| } | |||||
| } | |||||
| func (n *S2STransferNode) BypassFileInfoVar() dag.ValueOutputSlot { | |||||
| return dag.ValueOutputSlot{ | |||||
| Node: n, | |||||
| Index: 0, | |||||
| } | |||||
| } | |||||
| func (n *S2STransferNode) GenerateOp() (exec.Op, error) { | |||||
| return &S2STransfer{ | |||||
| Src: n.Src, | |||||
| SrcPath: n.SrcPathSlot().Var().VarID, | |||||
| Dst: n.Dst, | |||||
| Output: n.BypassFileInfoVar().Var().VarID, | |||||
| BypassCallback: n.BypassCallbackSlot().Var().VarID, | |||||
| }, nil | |||||
| } | |||||
| @@ -12,7 +12,7 @@ import ( | |||||
| "gitlink.org.cn/cloudream/common/utils/io2" | "gitlink.org.cn/cloudream/common/utils/io2" | ||||
| stgmod "gitlink.org.cn/cloudream/storage/common/models" | stgmod "gitlink.org.cn/cloudream/storage/common/models" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2" | "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/svcmgr" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/agtpool" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/types" | "gitlink.org.cn/cloudream/storage/common/pkgs/storage/types" | ||||
| ) | ) | ||||
| @@ -42,12 +42,12 @@ func (o *ShardRead) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| Debugf("reading from shard store") | Debugf("reading from shard store") | ||||
| defer logger.Debugf("reading from shard store finished") | defer logger.Debugf("reading from shard store finished") | ||||
| stgMgr, err := exec.GetValueByType[*svcmgr.Manager](ctx) | |||||
| stgAgts, err := exec.GetValueByType[*agtpool.AgentPool](ctx) | |||||
| if err != nil { | if err != nil { | ||||
| return fmt.Errorf("getting storage manager: %w", err) | return fmt.Errorf("getting storage manager: %w", err) | ||||
| } | } | ||||
| store, err := stgMgr.GetShardStore(o.StorageID) | |||||
| store, err := stgAgts.GetShardStore(o.StorageID) | |||||
| if err != nil { | if err != nil { | ||||
| return fmt.Errorf("getting shard store of storage %v: %w", o.StorageID, err) | return fmt.Errorf("getting shard store of storage %v: %w", o.StorageID, err) | ||||
| } | } | ||||
| @@ -84,12 +84,12 @@ func (o *ShardWrite) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| Debugf("writting file to shard store") | Debugf("writting file to shard store") | ||||
| defer logger.Debugf("write to shard store finished") | defer logger.Debugf("write to shard store finished") | ||||
| stgMgr, err := exec.GetValueByType[*svcmgr.Manager](ctx) | |||||
| stgAgts, err := exec.GetValueByType[*agtpool.AgentPool](ctx) | |||||
| if err != nil { | if err != nil { | ||||
| return fmt.Errorf("getting storage manager: %w", err) | return fmt.Errorf("getting storage manager: %w", err) | ||||
| } | } | ||||
| store, err := stgMgr.GetShardStore(o.StorageID) | |||||
| store, err := stgAgts.GetShardStore(o.StorageID) | |||||
| if err != nil { | if err != nil { | ||||
| return fmt.Errorf("getting shard store of storage %v: %w", o.StorageID, err) | return fmt.Errorf("getting shard store of storage %v: %w", o.StorageID, err) | ||||
| } | } | ||||
| @@ -7,8 +7,9 @@ import ( | |||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/logger" | "gitlink.org.cn/cloudream/common/pkgs/logger" | ||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | ||||
| stgmod "gitlink.org.cn/cloudream/storage/common/models" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2" | "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/svcmgr" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/agtpool" | |||||
| ) | ) | ||||
| func init() { | func init() { | ||||
| @@ -16,12 +17,9 @@ func init() { | |||||
| } | } | ||||
| type SharedLoad struct { | type SharedLoad struct { | ||||
| Input exec.VarID `json:"input"` | |||||
| StorageID cdssdk.StorageID `json:"storageID"` | |||||
| UserID cdssdk.UserID `json:"userID"` | |||||
| PackageID cdssdk.PackageID `json:"packageID"` | |||||
| Path string `json:"path"` | |||||
| FullPathOutput exec.VarID `json:"fullPathOutput"` | |||||
| Input exec.VarID | |||||
| StorageID cdssdk.StorageID | |||||
| ObjectPath string | |||||
| } | } | ||||
| func (o *SharedLoad) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | func (o *SharedLoad) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | ||||
| @@ -30,12 +28,12 @@ func (o *SharedLoad) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| Debugf("load file to shared store") | Debugf("load file to shared store") | ||||
| defer logger.Debugf("load file to shared store finished") | defer logger.Debugf("load file to shared store finished") | ||||
| stgMgr, err := exec.GetValueByType[*svcmgr.Manager](ctx) | |||||
| stgAgts, err := exec.GetValueByType[*agtpool.AgentPool](ctx) | |||||
| if err != nil { | if err != nil { | ||||
| return fmt.Errorf("getting storage manager: %w", err) | return fmt.Errorf("getting storage manager: %w", err) | ||||
| } | } | ||||
| store, err := stgMgr.GetSharedStore(o.StorageID) | |||||
| store, err := stgAgts.GetSharedStore(o.StorageID) | |||||
| if err != nil { | if err != nil { | ||||
| return fmt.Errorf("getting shard store of storage %v: %w", o.StorageID, err) | return fmt.Errorf("getting shard store of storage %v: %w", o.StorageID, err) | ||||
| } | } | ||||
| @@ -46,44 +44,29 @@ func (o *SharedLoad) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| } | } | ||||
| defer input.Stream.Close() | defer input.Stream.Close() | ||||
| fullPath, err := store.WritePackageObject(o.UserID, o.PackageID, o.Path, input.Stream) | |||||
| if err != nil { | |||||
| return fmt.Errorf("writing file to shard store: %w", err) | |||||
| } | |||||
| if o.FullPathOutput > 0 { | |||||
| e.PutVar(o.FullPathOutput, &exec.StringValue{ | |||||
| Value: fullPath, | |||||
| }) | |||||
| } | |||||
| return nil | |||||
| return store.Write(o.ObjectPath, input.Stream) | |||||
| } | } | ||||
| func (o *SharedLoad) String() string { | func (o *SharedLoad) String() string { | ||||
| return fmt.Sprintf("SharedLoad %v -> %v:%v/%v/%v", o.Input, o.StorageID, o.UserID, o.PackageID, o.Path) | |||||
| return fmt.Sprintf("SharedLoad %v -> %v:%v", o.Input, o.StorageID, o.ObjectPath) | |||||
| } | } | ||||
| type SharedLoadNode struct { | type SharedLoadNode struct { | ||||
| dag.NodeBase | dag.NodeBase | ||||
| To ioswitch2.To | |||||
| StorageID cdssdk.StorageID | |||||
| UserID cdssdk.UserID | |||||
| PackageID cdssdk.PackageID | |||||
| Path string | |||||
| To ioswitch2.To | |||||
| Storage stgmod.StorageDetail | |||||
| ObjectPath string | |||||
| } | } | ||||
| func (b *GraphNodeBuilder) NewSharedLoad(to ioswitch2.To, stgID cdssdk.StorageID, userID cdssdk.UserID, packageID cdssdk.PackageID, path string) *SharedLoadNode { | |||||
| func (b *GraphNodeBuilder) NewSharedLoad(to ioswitch2.To, stg stgmod.StorageDetail, objPath string) *SharedLoadNode { | |||||
| node := &SharedLoadNode{ | node := &SharedLoadNode{ | ||||
| To: to, | |||||
| StorageID: stgID, | |||||
| UserID: userID, | |||||
| PackageID: packageID, | |||||
| Path: path, | |||||
| To: to, | |||||
| Storage: stg, | |||||
| ObjectPath: objPath, | |||||
| } | } | ||||
| b.AddNode(node) | b.AddNode(node) | ||||
| node.InputStreams().Init(1) | node.InputStreams().Init(1) | ||||
| node.OutputValues().Init(node, 1) | |||||
| return node | return node | ||||
| } | } | ||||
| @@ -102,17 +85,10 @@ func (t *SharedLoadNode) Input() dag.StreamInputSlot { | |||||
| } | } | ||||
| } | } | ||||
| func (t *SharedLoadNode) FullPathVar() *dag.ValueVar { | |||||
| return t.OutputValues().Get(0) | |||||
| } | |||||
| func (t *SharedLoadNode) GenerateOp() (exec.Op, error) { | func (t *SharedLoadNode) GenerateOp() (exec.Op, error) { | ||||
| return &SharedLoad{ | return &SharedLoad{ | ||||
| Input: t.InputStreams().Get(0).VarID, | |||||
| StorageID: t.StorageID, | |||||
| UserID: t.UserID, | |||||
| PackageID: t.PackageID, | |||||
| Path: t.Path, | |||||
| FullPathOutput: t.OutputValues().Get(0).VarID, | |||||
| Input: t.InputStreams().Get(0).VarID, | |||||
| StorageID: t.Storage.Storage.StorageID, | |||||
| ObjectPath: t.ObjectPath, | |||||
| }, nil | }, nil | ||||
| } | } | ||||
| @@ -0,0 +1,488 @@ | |||||
| package gen | |||||
| import ( | |||||
| "fmt" | |||||
| "math" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/dag" | |||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | |||||
| "gitlink.org.cn/cloudream/common/utils/lo2" | |||||
| "gitlink.org.cn/cloudream/common/utils/math2" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/ops2" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/parser/state" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/types" | |||||
| ) | |||||
| // 检查使用不同编码时参数是否设置到位 | |||||
| func CheckEncodingParams(ctx *state.GenerateState) error { | |||||
| for _, f := range ctx.Ft.Froms { | |||||
| if f.GetStreamIndex().IsEC() { | |||||
| ctx.UseEC = true | |||||
| if ctx.Ft.ECParam == nil { | |||||
| return fmt.Errorf("EC encoding parameters not set") | |||||
| } | |||||
| } | |||||
| if f.GetStreamIndex().IsSegment() { | |||||
| ctx.UseSegment = true | |||||
| if ctx.Ft.SegmentParam == nil { | |||||
| return fmt.Errorf("segment parameters not set") | |||||
| } | |||||
| } | |||||
| } | |||||
| for _, t := range ctx.Ft.Toes { | |||||
| if t.GetStreamIndex().IsEC() { | |||||
| ctx.UseEC = true | |||||
| if ctx.Ft.ECParam == nil { | |||||
| return fmt.Errorf("EC encoding parameters not set") | |||||
| } | |||||
| } | |||||
| if t.GetStreamIndex().IsSegment() { | |||||
| ctx.UseSegment = true | |||||
| if ctx.Ft.SegmentParam == nil { | |||||
| return fmt.Errorf("segment parameters not set") | |||||
| } | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| // 计算输入流的打开范围。如果From或者To中包含EC的流,则会将打开范围扩大到条带大小的整数倍。 | |||||
| func CalcStreamRange(ctx *state.GenerateState) { | |||||
| rng := math2.NewRange(math.MaxInt64, 0) | |||||
| for _, to := range ctx.Ft.Toes { | |||||
| strIdx := to.GetStreamIndex() | |||||
| if strIdx.IsRaw() { | |||||
| toRng := to.GetRange() | |||||
| rng.ExtendStart(toRng.Offset) | |||||
| if toRng.Length != nil { | |||||
| rng.ExtendEnd(toRng.Offset + *toRng.Length) | |||||
| } else { | |||||
| rng.Length = nil | |||||
| } | |||||
| } else if strIdx.IsEC() { | |||||
| toRng := to.GetRange() | |||||
| stripSize := ctx.Ft.ECParam.StripSize() | |||||
| blkStartIndex := math2.FloorDiv(toRng.Offset, int64(ctx.Ft.ECParam.ChunkSize)) | |||||
| rng.ExtendStart(blkStartIndex * stripSize) | |||||
| if toRng.Length != nil { | |||||
| blkEndIndex := math2.CeilDiv(toRng.Offset+*toRng.Length, int64(ctx.Ft.ECParam.ChunkSize)) | |||||
| rng.ExtendEnd(blkEndIndex * stripSize) | |||||
| } else { | |||||
| rng.Length = nil | |||||
| } | |||||
| } else if strIdx.IsSegment() { | |||||
| // Segment节点的Range是相对于本段的,需要加上本段的起始位置 | |||||
| toRng := to.GetRange() | |||||
| segStart := ctx.Ft.SegmentParam.CalcSegmentStart(strIdx.Index) | |||||
| offset := toRng.Offset + segStart | |||||
| rng.ExtendStart(offset) | |||||
| if toRng.Length != nil { | |||||
| rng.ExtendEnd(offset + *toRng.Length) | |||||
| } else { | |||||
| rng.Length = nil | |||||
| } | |||||
| } | |||||
| } | |||||
| if ctx.UseEC { | |||||
| stripSize := ctx.Ft.ECParam.StripSize() | |||||
| rng.ExtendStart(math2.Floor(rng.Offset, stripSize)) | |||||
| if rng.Length != nil { | |||||
| rng.ExtendEnd(math2.Ceil(rng.Offset+*rng.Length, stripSize)) | |||||
| } | |||||
| } | |||||
| ctx.StreamRange = rng | |||||
| } | |||||
| func Extend(ctx *state.GenerateState) error { | |||||
| for _, fr := range ctx.Ft.Froms { | |||||
| frNode, err := buildFromNode(ctx, fr) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| ctx.FromNodes[fr] = frNode | |||||
| ctx.IndexedStreams = append(ctx.IndexedStreams, state.IndexedStream{ | |||||
| Stream: frNode.Output().Var(), | |||||
| StreamIndex: fr.GetStreamIndex(), | |||||
| }) | |||||
| // 对于完整文件的From,生成Split指令 | |||||
| if fr.GetStreamIndex().IsRaw() { | |||||
| // 只有输入输出需要EC编码的块时,才生成相关指令 | |||||
| if ctx.UseEC { | |||||
| splitNode := ctx.DAG.NewChunkedSplit(ctx.Ft.ECParam.ChunkSize, ctx.Ft.ECParam.K) | |||||
| splitNode.Split(frNode.Output().Var()) | |||||
| for i := 0; i < ctx.Ft.ECParam.K; i++ { | |||||
| ctx.IndexedStreams = append(ctx.IndexedStreams, state.IndexedStream{ | |||||
| Stream: splitNode.SubStream(i), | |||||
| StreamIndex: ioswitch2.ECStream(i), | |||||
| }) | |||||
| } | |||||
| } | |||||
| // 同上 | |||||
| if ctx.UseSegment { | |||||
| splitNode := ctx.DAG.NewSegmentSplit(ctx.Ft.SegmentParam.Segments) | |||||
| frNode.Output().Var().ToSlot(splitNode.InputSlot()) | |||||
| for i := 0; i < len(ctx.Ft.SegmentParam.Segments); i++ { | |||||
| ctx.IndexedStreams = append(ctx.IndexedStreams, state.IndexedStream{ | |||||
| Stream: splitNode.Segment(i), | |||||
| StreamIndex: ioswitch2.SegmentStream(i), | |||||
| }) | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| if ctx.UseEC { | |||||
| // 如果有K个不同的文件块流,则生成Multiply指令,同时针对其生成的流,生成Join指令 | |||||
| ecInputStrs := make(map[int]*dag.StreamVar) | |||||
| for _, s := range ctx.IndexedStreams { | |||||
| if s.StreamIndex.IsEC() && ecInputStrs[s.StreamIndex.Index] == nil { | |||||
| ecInputStrs[s.StreamIndex.Index] = s.Stream | |||||
| if len(ecInputStrs) == ctx.Ft.ECParam.K { | |||||
| break | |||||
| } | |||||
| } | |||||
| } | |||||
| if len(ecInputStrs) == ctx.Ft.ECParam.K { | |||||
| mulNode := ctx.DAG.NewECMultiply(*ctx.Ft.ECParam) | |||||
| for i, s := range ecInputStrs { | |||||
| mulNode.AddInput(s, i) | |||||
| } | |||||
| for i := 0; i < ctx.Ft.ECParam.N; i++ { | |||||
| ctx.IndexedStreams = append(ctx.IndexedStreams, state.IndexedStream{ | |||||
| Stream: mulNode.NewOutput(i), | |||||
| StreamIndex: ioswitch2.ECStream(i), | |||||
| }) | |||||
| } | |||||
| joinNode := ctx.DAG.NewChunkedJoin(ctx.Ft.ECParam.ChunkSize) | |||||
| for i := 0; i < ctx.Ft.ECParam.K; i++ { | |||||
| // 不可能找不到流 | |||||
| joinNode.AddInput(findOutputStream(ctx, ioswitch2.ECStream(i))) | |||||
| } | |||||
| ctx.IndexedStreams = append(ctx.IndexedStreams, state.IndexedStream{ | |||||
| Stream: joinNode.Joined(), | |||||
| StreamIndex: ioswitch2.RawStream(), | |||||
| }) | |||||
| } | |||||
| } | |||||
| if ctx.UseSegment { | |||||
| // 先假设有所有的顺序分段,生成Join指令,后续根据Range再实际计算是否缺少流 | |||||
| joinNode := ctx.DAG.NewSegmentJoin(ctx.Ft.SegmentParam.Segments) | |||||
| for i := 0; i < ctx.Ft.SegmentParam.SegmentCount(); i++ { | |||||
| str := findOutputStream(ctx, ioswitch2.SegmentStream(i)) | |||||
| if str != nil { | |||||
| str.ToSlot(joinNode.InputSlot(i)) | |||||
| } | |||||
| } | |||||
| ctx.IndexedStreams = append(ctx.IndexedStreams, state.IndexedStream{ | |||||
| Stream: joinNode.Joined(), | |||||
| StreamIndex: ioswitch2.RawStream(), | |||||
| }) | |||||
| // SegmentJoin生成的Join指令可以用来生成EC块 | |||||
| if ctx.UseEC { | |||||
| splitNode := ctx.DAG.NewChunkedSplit(ctx.Ft.ECParam.ChunkSize, ctx.Ft.ECParam.K) | |||||
| splitNode.Split(joinNode.Joined()) | |||||
| mulNode := ctx.DAG.NewECMultiply(*ctx.Ft.ECParam) | |||||
| for i := 0; i < ctx.Ft.ECParam.K; i++ { | |||||
| mulNode.AddInput(splitNode.SubStream(i), i) | |||||
| ctx.IndexedStreams = append(ctx.IndexedStreams, state.IndexedStream{ | |||||
| Stream: splitNode.SubStream(i), | |||||
| StreamIndex: ioswitch2.ECStream(i), | |||||
| }) | |||||
| } | |||||
| for i := 0; i < ctx.Ft.ECParam.N; i++ { | |||||
| ctx.IndexedStreams = append(ctx.IndexedStreams, state.IndexedStream{ | |||||
| Stream: mulNode.NewOutput(i), | |||||
| StreamIndex: ioswitch2.ECStream(i), | |||||
| }) | |||||
| } | |||||
| } | |||||
| } | |||||
| // 为每一个To找到一个输入流 | |||||
| for _, to := range ctx.Ft.Toes { | |||||
| toNode, err := buildToNode(ctx, to) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| ctx.ToNodes[to] = toNode | |||||
| str := findOutputStream(ctx, to.GetStreamIndex()) | |||||
| if str == nil { | |||||
| return fmt.Errorf("no output stream found for data index %d", to.GetStreamIndex()) | |||||
| } | |||||
| toNode.SetInput(str) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func buildFromNode(ctx *state.GenerateState, f ioswitch2.From) (ops2.FromNode, error) { | |||||
| var repRange math2.Range | |||||
| repRange.Offset = ctx.StreamRange.Offset | |||||
| if ctx.StreamRange.Length != nil { | |||||
| repRngLen := *ctx.StreamRange.Length | |||||
| repRange.Length = &repRngLen | |||||
| } | |||||
| var blkRange math2.Range | |||||
| if ctx.UseEC { | |||||
| blkRange.Offset = ctx.StreamRange.Offset / int64(ctx.Ft.ECParam.ChunkSize*ctx.Ft.ECParam.K) * int64(ctx.Ft.ECParam.ChunkSize) | |||||
| if ctx.StreamRange.Length != nil { | |||||
| blkRngLen := *ctx.StreamRange.Length / int64(ctx.Ft.ECParam.ChunkSize*ctx.Ft.ECParam.K) * int64(ctx.Ft.ECParam.ChunkSize) | |||||
| blkRange.Length = &blkRngLen | |||||
| } | |||||
| } | |||||
| switch f := f.(type) { | |||||
| case *ioswitch2.FromShardstore: | |||||
| t := ctx.DAG.NewShardRead(f, f.Storage.Storage.StorageID, types.NewOpen(f.FileHash)) | |||||
| if f.StreamIndex.IsRaw() { | |||||
| t.Open.WithNullableLength(repRange.Offset, repRange.Length) | |||||
| } else if f.StreamIndex.IsEC() { | |||||
| t.Open.WithNullableLength(blkRange.Offset, blkRange.Length) | |||||
| } else if f.StreamIndex.IsSegment() { | |||||
| segStart := ctx.Ft.SegmentParam.CalcSegmentStart(f.StreamIndex.Index) | |||||
| segLen := ctx.Ft.SegmentParam.Segments[f.StreamIndex.Index] | |||||
| segEnd := segStart + segLen | |||||
| // 打开的范围不超过本段的范围 | |||||
| openOff := ctx.StreamRange.Offset - segStart | |||||
| openOff = math2.Clamp(openOff, 0, segLen) | |||||
| openLen := segLen | |||||
| if ctx.StreamRange.Length != nil { | |||||
| strEnd := ctx.StreamRange.Offset + *ctx.StreamRange.Length | |||||
| openEnd := math2.Min(strEnd, segEnd) | |||||
| openLen = openEnd - segStart - openOff | |||||
| } | |||||
| t.Open.WithNullableLength(openOff, &openLen) | |||||
| } | |||||
| switch addr := f.Hub.Address.(type) { | |||||
| case *cdssdk.HttpAddressInfo: | |||||
| t.Env().ToEnvWorker(&ioswitch2.HttpHubWorker{Hub: f.Hub}) | |||||
| t.Env().Pinned = true | |||||
| case *cdssdk.GRPCAddressInfo: | |||||
| t.Env().ToEnvWorker(&ioswitch2.AgentWorker{Hub: f.Hub, Address: *addr}) | |||||
| t.Env().Pinned = true | |||||
| default: | |||||
| return nil, fmt.Errorf("unsupported node address type %T", addr) | |||||
| } | |||||
| return t, nil | |||||
| case *ioswitch2.FromDriver: | |||||
| n := ctx.DAG.NewFromDriver(f, f.Handle) | |||||
| n.Env().ToEnvDriver() | |||||
| n.Env().Pinned = true | |||||
| if f.StreamIndex.IsRaw() { | |||||
| f.Handle.RangeHint.Offset = repRange.Offset | |||||
| f.Handle.RangeHint.Length = repRange.Length | |||||
| } else if f.StreamIndex.IsEC() { | |||||
| f.Handle.RangeHint.Offset = blkRange.Offset | |||||
| f.Handle.RangeHint.Length = blkRange.Length | |||||
| } else if f.StreamIndex.IsSegment() { | |||||
| segStart := ctx.Ft.SegmentParam.CalcSegmentStart(f.StreamIndex.Index) | |||||
| segLen := ctx.Ft.SegmentParam.Segments[f.StreamIndex.Index] | |||||
| segEnd := segStart + segLen | |||||
| // 打开的范围不超过本段的范围 | |||||
| openOff := repRange.Offset - segStart | |||||
| openOff = math2.Clamp(openOff, 0, segLen) | |||||
| openLen := segLen | |||||
| if repRange.Length != nil { | |||||
| repEnd := repRange.Offset + *repRange.Length | |||||
| openEnd := math2.Min(repEnd, segEnd) | |||||
| openLen = openEnd - openOff | |||||
| } | |||||
| f.Handle.RangeHint.Offset = openOff | |||||
| f.Handle.RangeHint.Length = &openLen | |||||
| } | |||||
| return n, nil | |||||
| default: | |||||
| return nil, fmt.Errorf("unsupported from type %T", f) | |||||
| } | |||||
| } | |||||
| func buildToNode(ctx *state.GenerateState, t ioswitch2.To) (ops2.ToNode, error) { | |||||
| switch t := t.(type) { | |||||
| case *ioswitch2.ToShardStore: | |||||
| n := ctx.DAG.NewShardWrite(t, t.Storage, t.FileHashStoreKey) | |||||
| if err := setEnvByAddress(n, t.Hub, t.Hub.Address); err != nil { | |||||
| return nil, err | |||||
| } | |||||
| n.Env().Pinned = true | |||||
| return n, nil | |||||
| case *ioswitch2.ToDriver: | |||||
| n := ctx.DAG.NewToDriver(t, t.Handle) | |||||
| n.Env().ToEnvDriver() | |||||
| n.Env().Pinned = true | |||||
| return n, nil | |||||
| case *ioswitch2.LoadToShared: | |||||
| n := ctx.DAG.NewSharedLoad(t, t.Storage, t.ObjectPath) | |||||
| if err := setEnvByAddress(n, t.Hub, t.Hub.Address); err != nil { | |||||
| return nil, err | |||||
| } | |||||
| n.Env().Pinned = true | |||||
| return n, nil | |||||
| default: | |||||
| return nil, fmt.Errorf("unsupported to type %T", t) | |||||
| } | |||||
| } | |||||
| func setEnvByAddress(n dag.Node, hub cdssdk.Hub, addr cdssdk.HubAddressInfo) error { | |||||
| switch addr := addr.(type) { | |||||
| case *cdssdk.HttpAddressInfo: | |||||
| n.Env().ToEnvWorker(&ioswitch2.HttpHubWorker{Hub: hub}) | |||||
| case *cdssdk.GRPCAddressInfo: | |||||
| n.Env().ToEnvWorker(&ioswitch2.AgentWorker{Hub: hub, Address: *addr}) | |||||
| default: | |||||
| return fmt.Errorf("unsupported node address type %T", addr) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func findOutputStream(ctx *state.GenerateState, streamIndex ioswitch2.StreamIndex) *dag.StreamVar { | |||||
| var ret *dag.StreamVar | |||||
| for _, s := range ctx.IndexedStreams { | |||||
| if s.StreamIndex == streamIndex { | |||||
| ret = s.Stream | |||||
| break | |||||
| } | |||||
| } | |||||
| return ret | |||||
| } | |||||
| // 根据StreamRange,调整SegmentSplit中分段的个数和每段的大小 | |||||
| func FixSegmentSplit(ctx *state.GenerateState) error { | |||||
| var err error | |||||
| dag.WalkOnlyType[*ops2.SegmentSplitNode](ctx.DAG.Graph, func(node *ops2.SegmentSplitNode) bool { | |||||
| var strEnd *int64 | |||||
| if ctx.StreamRange.Length != nil { | |||||
| e := ctx.StreamRange.Offset + *ctx.StreamRange.Length | |||||
| strEnd = &e | |||||
| } | |||||
| startSeg, endSeg := ctx.Ft.SegmentParam.CalcSegmentRange(ctx.StreamRange.Offset, strEnd) | |||||
| // 关闭超出范围的分段 | |||||
| for i := endSeg; i < len(node.Segments); i++ { | |||||
| node.OutputStreams().Get(i).ClearAllDst() | |||||
| } | |||||
| node.OutputStreams().Slots.RemoveRange(endSeg, ctx.Ft.SegmentParam.SegmentCount()-endSeg) | |||||
| node.Segments = lo2.RemoveRange(node.Segments, endSeg, ctx.Ft.SegmentParam.SegmentCount()-endSeg) | |||||
| for i := 0; i < startSeg; i++ { | |||||
| node.OutputStreams().Get(i).ClearAllDst() | |||||
| } | |||||
| node.OutputStreams().Slots.RemoveRange(0, startSeg) | |||||
| node.Segments = lo2.RemoveRange(node.Segments, 0, startSeg) | |||||
| // StreamRange开始的位置可能在某个分段的中间,此时这个分段的大小等于流开始位置到分段结束位置的距离 | |||||
| startSegStart := ctx.Ft.SegmentParam.CalcSegmentStart(startSeg) | |||||
| node.Segments[0] -= ctx.StreamRange.Offset - startSegStart | |||||
| // StreamRange结束的位置可能在某个分段的中间,此时这个分段的大小就等于流结束位置到分段起始位置的距离 | |||||
| if strEnd != nil { | |||||
| endSegStart := ctx.Ft.SegmentParam.CalcSegmentStart(endSeg - 1) | |||||
| node.Segments[len(node.Segments)-1] = *strEnd - endSegStart | |||||
| } | |||||
| return true | |||||
| }) | |||||
| return err | |||||
| } | |||||
| // 从SegmentJoin中删除未使用的分段 | |||||
| func FixSegmentJoin(ctx *state.GenerateState) error { | |||||
| var err error | |||||
| dag.WalkOnlyType[*ops2.SegmentJoinNode](ctx.DAG.Graph, func(node *ops2.SegmentJoinNode) bool { | |||||
| start := ctx.StreamRange.Offset | |||||
| var end *int64 | |||||
| if ctx.StreamRange.Length != nil { | |||||
| e := ctx.StreamRange.Offset + *ctx.StreamRange.Length | |||||
| end = &e | |||||
| } | |||||
| startSeg, endSeg := ctx.Ft.SegmentParam.CalcSegmentRange(start, end) | |||||
| // 关闭超出范围的分段 | |||||
| for i := endSeg; i < len(node.Segments); i++ { | |||||
| node.InputStreams().Get(i).NotTo(node) | |||||
| } | |||||
| node.InputStreams().Slots.RemoveRange(endSeg, ctx.Ft.SegmentParam.SegmentCount()-endSeg) | |||||
| node.Segments = lo2.RemoveRange(node.Segments, endSeg, ctx.Ft.SegmentParam.SegmentCount()-endSeg) | |||||
| for i := 0; i < startSeg; i++ { | |||||
| node.InputStreams().Get(i).NotTo(node) | |||||
| } | |||||
| node.InputStreams().Slots.RemoveRange(0, startSeg) | |||||
| node.Segments = lo2.RemoveRange(node.Segments, 0, startSeg) | |||||
| // StreamRange开始的位置可能在某个分段的中间,此时这个分段的大小等于流开始位置到分段结束位置的距离 | |||||
| startSegStart := ctx.Ft.SegmentParam.CalcSegmentStart(startSeg) | |||||
| node.Segments[0] -= ctx.StreamRange.Offset - startSegStart | |||||
| // 检查一下必须的分段是否都被加入到Join中 | |||||
| for i := 0; i < node.InputStreams().Len(); i++ { | |||||
| if node.InputStreams().Get(i) == nil { | |||||
| err = fmt.Errorf("segment %v missed to join an raw stream", i+startSeg) | |||||
| return false | |||||
| } | |||||
| } | |||||
| return true | |||||
| }) | |||||
| return err | |||||
| } | |||||
| @@ -0,0 +1,98 @@ | |||||
| package opt | |||||
| import ( | |||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/dag" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/ops2" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/parser/state" | |||||
| ) | |||||
| // 删除输出流未被使用的Join指令 | |||||
| func RemoveUnusedJoin(ctx *state.GenerateState) bool { | |||||
| changed := false | |||||
| dag.WalkOnlyType[*ops2.ChunkedJoinNode](ctx.DAG.Graph, func(node *ops2.ChunkedJoinNode) bool { | |||||
| if node.Joined().Dst.Len() > 0 { | |||||
| return true | |||||
| } | |||||
| node.RemoveAllInputs() | |||||
| ctx.DAG.RemoveNode(node) | |||||
| return true | |||||
| }) | |||||
| return changed | |||||
| } | |||||
| // 删除未使用的Split指令 | |||||
| func RemoveUnusedSplit(ctx *state.GenerateState) bool { | |||||
| changed := false | |||||
| dag.WalkOnlyType[*ops2.ChunkedSplitNode](ctx.DAG.Graph, func(typ *ops2.ChunkedSplitNode) bool { | |||||
| // Split出来的每一个流都没有被使用,才能删除这个指令 | |||||
| for _, out := range typ.OutputStreams().Slots.RawArray() { | |||||
| if out.Dst.Len() > 0 { | |||||
| return true | |||||
| } | |||||
| } | |||||
| typ.RemoveAllStream() | |||||
| ctx.DAG.RemoveNode(typ) | |||||
| changed = true | |||||
| return true | |||||
| }) | |||||
| return changed | |||||
| } | |||||
| // 如果Split的结果被完全用于Join,则省略Split和Join指令 | |||||
| func OmitSplitJoin(ctx *state.GenerateState) bool { | |||||
| changed := false | |||||
| dag.WalkOnlyType[*ops2.ChunkedSplitNode](ctx.DAG.Graph, func(splitNode *ops2.ChunkedSplitNode) bool { | |||||
| // Split指令的每一个输出都有且只有一个目的地 | |||||
| var dstNode dag.Node | |||||
| for _, out := range splitNode.OutputStreams().Slots.RawArray() { | |||||
| if out.Dst.Len() != 1 { | |||||
| return true | |||||
| } | |||||
| if dstNode == nil { | |||||
| dstNode = out.Dst.Get(0) | |||||
| } else if dstNode != out.Dst.Get(0) { | |||||
| return true | |||||
| } | |||||
| } | |||||
| if dstNode == nil { | |||||
| return true | |||||
| } | |||||
| // 且这个目的地要是一个Join指令 | |||||
| joinNode, ok := dstNode.(*ops2.ChunkedJoinNode) | |||||
| if !ok { | |||||
| return true | |||||
| } | |||||
| // 同时这个Join指令的输入也必须全部来自Split指令的输出。 | |||||
| // 由于上面判断了Split指令的输出目的地都相同,所以这里只要判断Join指令的输入数量是否与Split指令的输出数量相同即可 | |||||
| if joinNode.InputStreams().Len() != splitNode.OutputStreams().Len() { | |||||
| return true | |||||
| } | |||||
| // 所有条件都满足,可以开始省略操作,将Join操作的目的地的输入流替换为Split操作的输入流: | |||||
| // F->Split->Join->T 变换为:F->T | |||||
| splitInput := splitNode.InputStreams().Get(0) | |||||
| for _, to := range joinNode.Joined().Dst.RawArray() { | |||||
| splitInput.To(to, to.InputStreams().IndexOf(joinNode.Joined())) | |||||
| } | |||||
| splitInput.NotTo(splitNode) | |||||
| // 并删除这两个指令 | |||||
| ctx.DAG.RemoveNode(joinNode) | |||||
| ctx.DAG.RemoveNode(splitNode) | |||||
| changed = true | |||||
| return true | |||||
| }) | |||||
| return changed | |||||
| } | |||||
| @@ -0,0 +1,38 @@ | |||||
| package opt | |||||
| import ( | |||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/dag" | |||||
| "gitlink.org.cn/cloudream/common/utils/lo2" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/ops2" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/parser/state" | |||||
| ) | |||||
| // 减少未使用的Multiply指令的输出流。如果减少到0,则删除该指令 | |||||
| func RemoveUnusedMultiplyOutput(ctx *state.GenerateState) bool { | |||||
| changed := false | |||||
| dag.WalkOnlyType[*ops2.ECMultiplyNode](ctx.DAG.Graph, func(node *ops2.ECMultiplyNode) bool { | |||||
| outArr := node.OutputStreams().Slots.RawArray() | |||||
| for i2, out := range outArr { | |||||
| if out.Dst.Len() > 0 { | |||||
| continue | |||||
| } | |||||
| outArr[i2] = nil | |||||
| node.OutputIndexes[i2] = -2 | |||||
| changed = true | |||||
| } | |||||
| node.OutputStreams().Slots.SetRawArray(lo2.RemoveAllDefault(outArr)) | |||||
| node.OutputIndexes = lo2.RemoveAll(node.OutputIndexes, -2) | |||||
| // 如果所有输出流都被删除,则删除该指令 | |||||
| if node.OutputStreams().Len() == 0 { | |||||
| node.RemoveAllInputs() | |||||
| ctx.DAG.RemoveNode(node) | |||||
| changed = true | |||||
| } | |||||
| return true | |||||
| }) | |||||
| return changed | |||||
| } | |||||
| @@ -0,0 +1,154 @@ | |||||
| package opt | |||||
| import ( | |||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/dag" | |||||
| "gitlink.org.cn/cloudream/common/utils/math2" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/ops2" | |||||
| "gitlink.org.cn/cloudream/storage/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 | |||||
| } | |||||
| if node.Output().Var().Dst.Len() == 0 { | |||||
| ctx.DAG.RemoveNode(node) | |||||
| } | |||||
| return true | |||||
| }) | |||||
| } | |||||
| // 对于所有未使用的流,增加Drop指令 | |||||
| func DropUnused(ctx *state.GenerateState) { | |||||
| ctx.DAG.Walk(func(node dag.Node) bool { | |||||
| for _, out := range node.OutputStreams().Slots.RawArray() { | |||||
| if out.Dst.Len() == 0 { | |||||
| n := ctx.DAG.NewDropStream() | |||||
| *n.Env() = *node.Env() | |||||
| n.SetInput(out) | |||||
| } | |||||
| } | |||||
| return true | |||||
| }) | |||||
| } | |||||
| // 为IPFS写入指令存储结果 | |||||
| func StoreShardWriteResult(ctx *state.GenerateState) { | |||||
| dag.WalkOnlyType[*ops2.ShardWriteNode](ctx.DAG.Graph, func(n *ops2.ShardWriteNode) bool { | |||||
| if n.FileHashStoreKey == "" { | |||||
| return true | |||||
| } | |||||
| storeNode := ctx.DAG.NewStore() | |||||
| storeNode.Env().ToEnvDriver() | |||||
| storeNode.Store(n.FileHashStoreKey, n.FileHashVar()) | |||||
| return true | |||||
| }) | |||||
| dag.WalkOnlyType[*ops2.BypassToShardStoreNode](ctx.DAG.Graph, func(n *ops2.BypassToShardStoreNode) bool { | |||||
| if n.FileHashStoreKey == "" { | |||||
| return true | |||||
| } | |||||
| storeNode := ctx.DAG.NewStore() | |||||
| storeNode.Env().ToEnvDriver() | |||||
| storeNode.Store(n.FileHashStoreKey, n.FileHashVar().Var()) | |||||
| return true | |||||
| }) | |||||
| } | |||||
| // 生成Range指令。StreamRange可能超过文件总大小,但Range指令会在数据量不够时不报错而是正常返回 | |||||
| func GenerateRange(ctx *state.GenerateState) { | |||||
| for to, toNode := range ctx.ToNodes { | |||||
| toStrIdx := to.GetStreamIndex() | |||||
| toRng := to.GetRange() | |||||
| if toStrIdx.IsRaw() { | |||||
| n := ctx.DAG.NewRange() | |||||
| toInput := toNode.Input() | |||||
| *n.Env() = *toInput.Var().Src.Env() | |||||
| rnged := n.RangeStream(toInput.Var(), math2.Range{ | |||||
| Offset: toRng.Offset - ctx.StreamRange.Offset, | |||||
| Length: toRng.Length, | |||||
| }) | |||||
| toInput.Var().NotTo(toNode) | |||||
| toNode.SetInput(rnged) | |||||
| } else if toStrIdx.IsEC() { | |||||
| stripSize := int64(ctx.Ft.ECParam.ChunkSize * ctx.Ft.ECParam.K) | |||||
| blkStartIdx := ctx.StreamRange.Offset / stripSize | |||||
| blkStart := blkStartIdx * int64(ctx.Ft.ECParam.ChunkSize) | |||||
| n := ctx.DAG.NewRange() | |||||
| toInput := toNode.Input() | |||||
| *n.Env() = *toInput.Var().Src.Env() | |||||
| rnged := n.RangeStream(toInput.Var(), math2.Range{ | |||||
| Offset: toRng.Offset - blkStart, | |||||
| Length: toRng.Length, | |||||
| }) | |||||
| toInput.Var().NotTo(toNode) | |||||
| toNode.SetInput(rnged) | |||||
| } else if toStrIdx.IsSegment() { | |||||
| // if frNode, ok := toNode.Input().Var().From().Node.(ops2.FromNode); ok { | |||||
| // // 目前只有To也是分段时,才可能对接一个提供分段的From,此时不需要再生成Range指令 | |||||
| // if frNode.GetFrom().GetStreamIndex().IsSegment() { | |||||
| // continue | |||||
| // } | |||||
| // } | |||||
| // segStart := ctx.Ft.SegmentParam.CalcSegmentStart(toStrIdx.Index) | |||||
| // strStart := segStart + toRng.Offset | |||||
| // n := ctx.DAG.NewRange() | |||||
| // toInput := toNode.Input() | |||||
| // *n.Env() = *toInput.Var().From().Node.Env() | |||||
| // rnged := n.RangeStream(toInput.Var(), exec.Range{ | |||||
| // Offset: strStart - ctx.StreamRange.Offset, | |||||
| // Length: toRng.Length, | |||||
| // }) | |||||
| // toInput.Var().NotTo(toNode, toInput.Index) | |||||
| // toNode.SetInput(rnged) | |||||
| } | |||||
| } | |||||
| } | |||||
| // 生成Clone指令 | |||||
| func GenerateClone(ctx *state.GenerateState) { | |||||
| ctx.DAG.Walk(func(node dag.Node) bool { | |||||
| for _, outVar := range node.OutputStreams().Slots.RawArray() { | |||||
| if outVar.Dst.Len() <= 1 { | |||||
| continue | |||||
| } | |||||
| c := ctx.DAG.NewCloneStream() | |||||
| *c.Env() = *node.Env() | |||||
| for _, dst := range outVar.Dst.RawArray() { | |||||
| c.NewOutput().To(dst, dst.InputStreams().IndexOf(outVar)) | |||||
| } | |||||
| outVar.Dst.Resize(0) | |||||
| c.SetInput(outVar) | |||||
| } | |||||
| for _, outVar := range node.OutputValues().Slots.RawArray() { | |||||
| if outVar.Dst.Len() <= 1 { | |||||
| continue | |||||
| } | |||||
| t := ctx.DAG.NewCloneValue() | |||||
| *t.Env() = *node.Env() | |||||
| for _, dst := range outVar.Dst.RawArray() { | |||||
| t.NewOutput().To(dst, dst.InputValues().IndexOf(outVar)) | |||||
| } | |||||
| outVar.Dst.Resize(0) | |||||
| t.SetInput(outVar) | |||||
| } | |||||
| return true | |||||
| }) | |||||
| } | |||||
| @@ -0,0 +1,105 @@ | |||||
| package opt | |||||
| import ( | |||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/dag" | |||||
| "gitlink.org.cn/cloudream/common/utils/math2" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/ops2" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/parser/state" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/factory" | |||||
| ) | |||||
| // 将SegmentJoin指令替换成分片上传指令 | |||||
| func UseMultipartUploadToShardStore(ctx *state.GenerateState) { | |||||
| dag.WalkOnlyType[*ops2.SegmentJoinNode](ctx.DAG.Graph, func(joinNode *ops2.SegmentJoinNode) bool { | |||||
| if joinNode.Joined().Dst.Len() != 1 { | |||||
| return true | |||||
| } | |||||
| joinDst := joinNode.Joined().Dst.Get(0) | |||||
| shardNode, ok := joinDst.(*ops2.ShardWriteNode) | |||||
| if !ok { | |||||
| return true | |||||
| } | |||||
| // SegmentJoin的输出流的范围必须与ToShardStore的输入流的范围相同, | |||||
| // 虽然可以通过调整SegmentJoin的输入流来调整范围,但太复杂,暂不支持 | |||||
| toStrIdx := shardNode.GetTo().GetStreamIndex() | |||||
| toStrRng := shardNode.GetTo().GetRange() | |||||
| if toStrIdx.IsRaw() { | |||||
| if !toStrRng.Equals(ctx.StreamRange) { | |||||
| return true | |||||
| } | |||||
| } else { | |||||
| return true | |||||
| } | |||||
| // Join的目的地必须支持MultipartUpload功能才能替换成分片上传 | |||||
| multiUpload, err := factory.GetBuilder(shardNode.Storage).CreateMultiparter() | |||||
| if err != nil { | |||||
| return true | |||||
| } | |||||
| // Join的每一个段的大小必须超过最小分片大小。 | |||||
| // 目前只支持拆分超过最大分片的流,不支持合并多个小段流以达到最小分片大小。 | |||||
| for _, size := range joinNode.Segments { | |||||
| if size < multiUpload.MinPartSize() { | |||||
| return true | |||||
| } | |||||
| } | |||||
| initNode := ctx.DAG.NewMultipartInitiator(shardNode.Storage) | |||||
| initNode.Env().CopyFrom(shardNode.Env()) | |||||
| partNumber := 1 | |||||
| for i, size := range joinNode.Segments { | |||||
| joinInput := joinNode.InputSlot(i) | |||||
| if size > multiUpload.MaxPartSize() { | |||||
| // 如果一个分段的大小大于最大分片大小,则需要拆分为多个小段上传 | |||||
| // 拆分以及上传指令直接在流的产生节点执行 | |||||
| splits := math2.SplitLessThan(size, multiUpload.MaxPartSize()) | |||||
| splitNode := ctx.DAG.NewSegmentSplit(splits) | |||||
| splitNode.Env().CopyFrom(joinInput.Var().Src.Env()) | |||||
| joinInput.Var().ToSlot(splitNode.InputSlot()) | |||||
| for i2 := 0; i2 < len(splits); i2++ { | |||||
| uploadNode := ctx.DAG.NewMultipartUpload(shardNode.Storage, partNumber, splits[i2]) | |||||
| uploadNode.Env().CopyFrom(joinInput.Var().Src.Env()) | |||||
| initNode.UploadArgsVar().ToSlot(uploadNode.UploadArgsSlot()) | |||||
| splitNode.SegmentVar(i2).ToSlot(uploadNode.PartStreamSlot()) | |||||
| uploadNode.UploadResultVar().ToSlot(initNode.AppendPartInfoSlot()) | |||||
| partNumber++ | |||||
| } | |||||
| } else { | |||||
| // 否则直接上传整个分段 | |||||
| uploadNode := ctx.DAG.NewMultipartUpload(shardNode.Storage, partNumber, size) | |||||
| // 上传指令直接在流的产生节点执行 | |||||
| uploadNode.Env().CopyFrom(joinInput.Var().Src.Env()) | |||||
| initNode.UploadArgsVar().ToSlot(uploadNode.UploadArgsSlot()) | |||||
| joinInput.Var().ToSlot(uploadNode.PartStreamSlot()) | |||||
| uploadNode.UploadResultVar().ToSlot(initNode.AppendPartInfoSlot()) | |||||
| partNumber++ | |||||
| } | |||||
| joinInput.Var().NotTo(joinNode) | |||||
| } | |||||
| bypassNode := ctx.DAG.NewBypassToShardStore(shardNode.Storage.Storage.StorageID, shardNode.FileHashStoreKey) | |||||
| bypassNode.Env().CopyFrom(shardNode.Env()) | |||||
| // 分片上传Node产生的结果送到bypassNode,bypassNode将处理结果再送回分片上传Node | |||||
| initNode.BypassFileInfoVar().ToSlot(bypassNode.BypassFileInfoSlot()) | |||||
| bypassNode.BypassCallbackVar().ToSlot(initNode.BypassCallbackSlot()) | |||||
| // 最后删除Join指令和ToShardStore指令 | |||||
| ctx.DAG.RemoveNode(joinNode) | |||||
| ctx.DAG.RemoveNode(shardNode) | |||||
| delete(ctx.ToNodes, shardNode.GetTo()) | |||||
| return true | |||||
| }) | |||||
| } | |||||
| @@ -0,0 +1,69 @@ | |||||
| package opt | |||||
| import ( | |||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/dag" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/parser/state" | |||||
| ) | |||||
| // 通过流的输入输出位置来确定指令的执行位置。 | |||||
| // To系列的指令都会有固定的执行位置,这些位置会随着pin操作逐步扩散到整个DAG, | |||||
| // 所以理论上不会出现有指令的位置始终无法确定的情况。 | |||||
| func Pin(ctx *state.GenerateState) bool { | |||||
| changed := false | |||||
| ctx.DAG.Walk(func(node dag.Node) bool { | |||||
| if node.Env().Pinned { | |||||
| return true | |||||
| } | |||||
| var toEnv *dag.NodeEnv | |||||
| for _, out := range node.OutputStreams().Slots.RawArray() { | |||||
| for _, to := range out.Dst.RawArray() { | |||||
| if to.Env().Type == dag.EnvUnknown { | |||||
| continue | |||||
| } | |||||
| if toEnv == nil { | |||||
| toEnv = to.Env() | |||||
| } else if !toEnv.Equals(to.Env()) { | |||||
| toEnv = nil | |||||
| break | |||||
| } | |||||
| } | |||||
| } | |||||
| if toEnv != nil { | |||||
| if !node.Env().Equals(toEnv) { | |||||
| changed = true | |||||
| } | |||||
| *node.Env() = *toEnv | |||||
| return true | |||||
| } | |||||
| // 否则根据输入流的始发地来固定 | |||||
| var fromEnv *dag.NodeEnv | |||||
| for _, in := range node.InputStreams().Slots.RawArray() { | |||||
| if in.Src.Env().Type == dag.EnvUnknown { | |||||
| continue | |||||
| } | |||||
| if fromEnv == nil { | |||||
| fromEnv = in.Src.Env() | |||||
| } else if !fromEnv.Equals(in.Src.Env()) { | |||||
| fromEnv = nil | |||||
| break | |||||
| } | |||||
| } | |||||
| if fromEnv != nil { | |||||
| if !node.Env().Equals(fromEnv) { | |||||
| changed = true | |||||
| } | |||||
| *node.Env() = *fromEnv | |||||
| } | |||||
| return true | |||||
| }) | |||||
| return changed | |||||
| } | |||||
| @@ -0,0 +1,131 @@ | |||||
| package opt | |||||
| import ( | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/ops2" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/parser/state" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/factory" | |||||
| ) | |||||
| // 将直接从一个存储服务传到另一个存储服务的过程换成S2S传输 | |||||
| func UseS2STransfer(ctx *state.GenerateState) { | |||||
| // S2S传输暂不支持只传文件的一部分 | |||||
| if ctx.StreamRange.Offset != 0 || ctx.StreamRange.Length != nil { | |||||
| return | |||||
| } | |||||
| for fr, frNode := range ctx.FromNodes { | |||||
| fromShard, ok := fr.(*ioswitch2.FromShardstore) | |||||
| if !ok { | |||||
| continue | |||||
| } | |||||
| fromStgBld := factory.GetBuilder(fromShard.Storage) | |||||
| if !fromStgBld.ShardStoreDesc().HasBypassRead() { | |||||
| continue | |||||
| } | |||||
| s2s, err := fromStgBld.CreateS2STransfer() | |||||
| if err != nil { | |||||
| continue | |||||
| } | |||||
| // 此输出流的所有目的地都要能支持S2S传输 | |||||
| outVar := frNode.Output().Var() | |||||
| if outVar.Dst.Len() == 0 { | |||||
| continue | |||||
| } | |||||
| failed := false | |||||
| var toShards []*ops2.ShardWriteNode | |||||
| // var toShareds []*ops2.SharedLoadNode | |||||
| loop: | |||||
| for i := 0; i < outVar.Dst.Len(); i++ { | |||||
| dstNode := outVar.Dst.Get(i) | |||||
| switch dstNode := dstNode.(type) { | |||||
| case *ops2.ShardWriteNode: | |||||
| dstStgBld := factory.GetBuilder(dstNode.Storage) | |||||
| if !dstStgBld.ShardStoreDesc().HasBypassWrite() { | |||||
| failed = true | |||||
| break | |||||
| } | |||||
| if !s2s.CanTransfer(dstNode.Storage) { | |||||
| failed = true | |||||
| break | |||||
| } | |||||
| toShards = append(toShards, dstNode) | |||||
| /* TODO 暂不支持共享存储服务 | |||||
| case *ops2.SharedLoadNode: | |||||
| if !s2s.CanTransfer(to.Storage) { | |||||
| failed = true | |||||
| break | |||||
| } | |||||
| toShareds = append(toShareds, to) | |||||
| */ | |||||
| default: | |||||
| failed = true | |||||
| break loop | |||||
| } | |||||
| } | |||||
| if failed { | |||||
| continue | |||||
| } | |||||
| for _, toShard := range toShards { | |||||
| s2sNode := ctx.DAG.NewS2STransfer(fromShard.Storage, toShard.Storage) | |||||
| // 直传指令在目的地Hub上执行 | |||||
| s2sNode.Env().CopyFrom(toShard.Env()) | |||||
| // 先获取文件路径,送到S2S节点 | |||||
| brNode := ctx.DAG.NewBypassFromShardStore(fromShard.Storage.Storage.StorageID, fromShard.FileHash) | |||||
| brNode.Env().CopyFrom(frNode.Env()) | |||||
| brNode.FilePathVar().ToSlot(s2sNode.SrcPathSlot()) | |||||
| // 传输结果通知目的节点 | |||||
| to := toShard.To.(*ioswitch2.ToShardStore) | |||||
| bwNode := ctx.DAG.NewBypassToShardStore(toShard.Storage.Storage.StorageID, to.FileHashStoreKey) | |||||
| bwNode.Env().CopyFrom(toShard.Env()) | |||||
| s2sNode.BypassFileInfoVar().ToSlot(bwNode.BypassFileInfoSlot()) | |||||
| bwNode.BypassCallbackVar().ToSlot(s2sNode.BypassCallbackSlot()) | |||||
| // 从计划中删除目标节点 | |||||
| ctx.DAG.RemoveNode(toShard) | |||||
| delete(ctx.ToNodes, toShard.To) | |||||
| } | |||||
| /* | |||||
| for _, toShared := range toShareds { | |||||
| s2sNode := ctx.DAG.NewS2STransfer(fromShard.Storage, toShared.Storage) | |||||
| // 直传指令在目的地Hub上执行 | |||||
| s2sNode.Env().CopyFrom(toShared.Env()) | |||||
| // 先获取文件路径,送到S2S节点 | |||||
| brNode := ctx.DAG.NewBypassFromShardStore(fromShard.Storage.Storage.StorageID, fromShard.FileHash) | |||||
| brNode.Env().CopyFrom(toShared.Env()) | |||||
| brNode.FilePathVar().ToSlot(s2sNode.SrcPathSlot()) | |||||
| // 传输结果通知目的节点 | |||||
| to := toShared.To.(*ioswitch2.LoadToShared) | |||||
| bwNode := ctx.DAG.NewBypassToShardStore(toShard.Storage.Storage.StorageID, to.FileHashStoreKey) | |||||
| bwNode.Env().CopyFrom(toShard.Env()) | |||||
| s2sNode.BypassFileInfoVar().ToSlot(bwNode.BypassFileInfoSlot()) | |||||
| bwNode.BypassCallbackVar().ToSlot(s2sNode.BypassCallbackSlot()) | |||||
| // 从计划中删除目标节点 | |||||
| ctx.DAG.RemoveNode(toShared) | |||||
| delete(ctx.ToNodes, toShared.To) | |||||
| } | |||||
| */ | |||||
| // 从计划中删除源节点 | |||||
| ctx.DAG.RemoveNode(frNode) | |||||
| delete(ctx.FromNodes, fr) | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,98 @@ | |||||
| package opt | |||||
| import ( | |||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/dag" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/ops2" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/parser/state" | |||||
| ) | |||||
| // 删除未使用的SegmentJoin | |||||
| func RemoveUnusedSegmentJoin(ctx *state.GenerateState) bool { | |||||
| changed := false | |||||
| dag.WalkOnlyType[*ops2.SegmentJoinNode](ctx.DAG.Graph, func(node *ops2.SegmentJoinNode) bool { | |||||
| if node.Joined().Dst.Len() > 0 { | |||||
| return true | |||||
| } | |||||
| node.RemoveAllInputs() | |||||
| ctx.DAG.RemoveNode(node) | |||||
| return true | |||||
| }) | |||||
| return changed | |||||
| } | |||||
| // 删除未使用的SegmentSplit | |||||
| func RemoveUnusedSegmentSplit(ctx *state.GenerateState) bool { | |||||
| changed := false | |||||
| dag.WalkOnlyType[*ops2.SegmentSplitNode](ctx.DAG.Graph, func(typ *ops2.SegmentSplitNode) bool { | |||||
| // Split出来的每一个流都没有被使用,才能删除这个指令 | |||||
| for _, out := range typ.OutputStreams().Slots.RawArray() { | |||||
| if out.Dst.Len() > 0 { | |||||
| return true | |||||
| } | |||||
| } | |||||
| typ.RemoveAllStream() | |||||
| ctx.DAG.RemoveNode(typ) | |||||
| changed = true | |||||
| return true | |||||
| }) | |||||
| return changed | |||||
| } | |||||
| // 如果Split的结果被完全用于Join,则省略Split和Join指令 | |||||
| func OmitSegmentSplitJoin(ctx *state.GenerateState) bool { | |||||
| changed := false | |||||
| dag.WalkOnlyType[*ops2.SegmentSplitNode](ctx.DAG.Graph, func(splitNode *ops2.SegmentSplitNode) bool { | |||||
| // 随便找一个输出流的目的地 | |||||
| splitOut := splitNode.OutputStreams().Get(0) | |||||
| if splitOut.Dst.Len() != 1 { | |||||
| return true | |||||
| } | |||||
| dstNode := splitOut.Dst.Get(0) | |||||
| // 这个目的地要是一个Join指令 | |||||
| joinNode, ok := dstNode.(*ops2.SegmentJoinNode) | |||||
| if !ok { | |||||
| return true | |||||
| } | |||||
| if splitNode.OutputStreams().Len() != joinNode.Joined().Dst.Len() { | |||||
| return true | |||||
| } | |||||
| // Join指令的输入必须全部来自Split指令的输出,且位置要相同 | |||||
| for i := 0; i < splitNode.OutputStreams().Len(); i++ { | |||||
| splitOut := splitNode.OutputStreams().Get(i) | |||||
| joinIn := joinNode.InputStreams().Get(i) | |||||
| if splitOut != joinIn { | |||||
| return true | |||||
| } | |||||
| if splitOut != nil && splitOut.Dst.Len() != 1 { | |||||
| return true | |||||
| } | |||||
| } | |||||
| // 所有条件都满足,可以开始省略操作,将Join操作的目的地的输入流替换为Split操作的输入流: | |||||
| // F->Split->Join->T 变换为:F->T | |||||
| splitInput := splitNode.InputStreams().Get(0) | |||||
| for _, to := range joinNode.Joined().Dst.RawArray() { | |||||
| splitInput.To(to, to.InputStreams().IndexOf(joinNode.Joined())) | |||||
| } | |||||
| splitInput.NotTo(splitNode) | |||||
| // 并删除这两个指令 | |||||
| ctx.DAG.RemoveNode(joinNode) | |||||
| ctx.DAG.RemoveNode(splitNode) | |||||
| changed = true | |||||
| return true | |||||
| }) | |||||
| return changed | |||||
| } | |||||
| @@ -0,0 +1,35 @@ | |||||
| package state | |||||
| import ( | |||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/dag" | |||||
| "gitlink.org.cn/cloudream/common/utils/math2" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch2/ops2" | |||||
| ) | |||||
| type IndexedStream struct { | |||||
| Stream *dag.StreamVar | |||||
| StreamIndex ioswitch2.StreamIndex | |||||
| } | |||||
| type GenerateState struct { | |||||
| Ft ioswitch2.FromTo | |||||
| DAG *ops2.GraphNodeBuilder | |||||
| // 为了产生所有To所需的数据范围,而需要From打开的范围。 | |||||
| // 这个范围是基于整个文件的,且上下界都取整到条带大小的整数倍,因此上界是有可能超过文件大小的。 | |||||
| ToNodes map[ioswitch2.To]ops2.ToNode | |||||
| FromNodes map[ioswitch2.From]ops2.FromNode | |||||
| IndexedStreams []IndexedStream | |||||
| StreamRange math2.Range | |||||
| UseEC bool // 是否使用纠删码 | |||||
| UseSegment bool // 是否使用分段 | |||||
| } | |||||
| func InitGenerateState(ft ioswitch2.FromTo) *GenerateState { | |||||
| return &GenerateState{ | |||||
| Ft: ft, | |||||
| DAG: ops2.NewGraphNodeBuilder(), | |||||
| ToNodes: make(map[ioswitch2.To]ops2.ToNode), | |||||
| FromNodes: make(map[ioswitch2.From]ops2.FromNode), | |||||
| } | |||||
| } | |||||
| @@ -3,6 +3,7 @@ package ioswitchlrc | |||||
| import ( | import ( | ||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | ||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | ||||
| "gitlink.org.cn/cloudream/common/utils/math2" | |||||
| ) | ) | ||||
| type From interface { | type From interface { | ||||
| @@ -13,7 +14,7 @@ type To interface { | |||||
| // To所需要的文件流的范围。具体含义与DataIndex有关系: | // To所需要的文件流的范围。具体含义与DataIndex有关系: | ||||
| // 如果DataIndex == -1,则表示在整个文件的范围。 | // 如果DataIndex == -1,则表示在整个文件的范围。 | ||||
| // 如果DataIndex >= 0,则表示在文件的某个分片的范围。 | // 如果DataIndex >= 0,则表示在文件的某个分片的范围。 | ||||
| GetRange() exec.Range | |||||
| GetRange() math2.Range | |||||
| GetDataIndex() int | GetDataIndex() int | ||||
| } | } | ||||
| @@ -24,7 +25,7 @@ type FromDriver struct { | |||||
| func NewFromDriver(dataIndex int) (*FromDriver, *exec.DriverWriteStream) { | func NewFromDriver(dataIndex int) (*FromDriver, *exec.DriverWriteStream) { | ||||
| handle := &exec.DriverWriteStream{ | handle := &exec.DriverWriteStream{ | ||||
| RangeHint: &exec.Range{}, | |||||
| RangeHint: &math2.Range{}, | |||||
| } | } | ||||
| return &FromDriver{ | return &FromDriver{ | ||||
| Handle: handle, | Handle: handle, | ||||
| @@ -58,7 +59,7 @@ func (f *FromNode) GetDataIndex() int { | |||||
| type ToDriver struct { | type ToDriver struct { | ||||
| Handle *exec.DriverReadStream | Handle *exec.DriverReadStream | ||||
| DataIndex int | DataIndex int | ||||
| Range exec.Range | |||||
| Range math2.Range | |||||
| } | } | ||||
| func NewToDriver(dataIndex int) (*ToDriver, *exec.DriverReadStream) { | func NewToDriver(dataIndex int) (*ToDriver, *exec.DriverReadStream) { | ||||
| @@ -69,7 +70,7 @@ func NewToDriver(dataIndex int) (*ToDriver, *exec.DriverReadStream) { | |||||
| }, &str | }, &str | ||||
| } | } | ||||
| func NewToDriverWithRange(dataIndex int, rng exec.Range) (*ToDriver, *exec.DriverReadStream) { | |||||
| func NewToDriverWithRange(dataIndex int, rng math2.Range) (*ToDriver, *exec.DriverReadStream) { | |||||
| str := exec.DriverReadStream{} | str := exec.DriverReadStream{} | ||||
| return &ToDriver{ | return &ToDriver{ | ||||
| Handle: &str, | Handle: &str, | ||||
| @@ -82,7 +83,7 @@ func (t *ToDriver) GetDataIndex() int { | |||||
| return t.DataIndex | return t.DataIndex | ||||
| } | } | ||||
| func (t *ToDriver) GetRange() exec.Range { | |||||
| func (t *ToDriver) GetRange() math2.Range { | |||||
| return t.Range | return t.Range | ||||
| } | } | ||||
| @@ -90,7 +91,7 @@ type ToNode struct { | |||||
| Hub cdssdk.Hub | Hub cdssdk.Hub | ||||
| Storage cdssdk.Storage | Storage cdssdk.Storage | ||||
| DataIndex int | DataIndex int | ||||
| Range exec.Range | |||||
| Range math2.Range | |||||
| FileHashStoreKey string | FileHashStoreKey string | ||||
| } | } | ||||
| @@ -103,7 +104,7 @@ func NewToStorage(hub cdssdk.Hub, stg cdssdk.Storage, dataIndex int, fileHashSto | |||||
| } | } | ||||
| } | } | ||||
| func NewToStorageWithRange(hub cdssdk.Hub, stg cdssdk.Storage, dataIndex int, fileHashStoreKey string, rng exec.Range) *ToNode { | |||||
| func NewToStorageWithRange(hub cdssdk.Hub, stg cdssdk.Storage, dataIndex int, fileHashStoreKey string, rng math2.Range) *ToNode { | |||||
| return &ToNode{ | return &ToNode{ | ||||
| Hub: hub, | Hub: hub, | ||||
| Storage: stg, | Storage: stg, | ||||
| @@ -117,7 +118,7 @@ func (t *ToNode) GetDataIndex() int { | |||||
| return t.DataIndex | return t.DataIndex | ||||
| } | } | ||||
| func (t *ToNode) GetRange() exec.Range { | |||||
| func (t *ToNode) GetRange() math2.Range { | |||||
| return t.Range | return t.Range | ||||
| } | } | ||||
| @@ -37,7 +37,10 @@ func (o *ChunkedSplit) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| sem := semaphore.NewWeighted(int64(len(outputs))) | sem := semaphore.NewWeighted(int64(len(outputs))) | ||||
| for i := range outputs { | for i := range outputs { | ||||
| sem.Acquire(ctx.Context, 1) | |||||
| err = sem.Acquire(ctx.Context, 1) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| e.PutVar(o.Outputs[i], &exec.StreamValue{ | e.PutVar(o.Outputs[i], &exec.StreamValue{ | ||||
| Stream: io2.AfterReadClosedOnce(outputs[i], func(closer io.ReadCloser) { | Stream: io2.AfterReadClosedOnce(outputs[i], func(closer io.ReadCloser) { | ||||
| @@ -81,7 +81,7 @@ func (o *Range) String() string { | |||||
| type RangeNode struct { | type RangeNode struct { | ||||
| dag.NodeBase | dag.NodeBase | ||||
| Range exec.Range | |||||
| Range math2.Range | |||||
| } | } | ||||
| func (b *GraphNodeBuilder) NewRange() *RangeNode { | func (b *GraphNodeBuilder) NewRange() *RangeNode { | ||||
| @@ -93,7 +93,7 @@ func (b *GraphNodeBuilder) NewRange() *RangeNode { | |||||
| return node | return node | ||||
| } | } | ||||
| func (t *RangeNode) RangeStream(input *dag.StreamVar, rng exec.Range) *dag.StreamVar { | |||||
| func (t *RangeNode) RangeStream(input *dag.StreamVar, rng math2.Range) *dag.StreamVar { | |||||
| input.To(t, 0) | input.To(t, 0) | ||||
| t.Range = rng | t.Range = rng | ||||
| return t.OutputStreams().Get(0) | return t.OutputStreams().Get(0) | ||||
| @@ -11,7 +11,7 @@ import ( | |||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | ||||
| "gitlink.org.cn/cloudream/common/utils/io2" | "gitlink.org.cn/cloudream/common/utils/io2" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitchlrc" | "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitchlrc" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/svcmgr" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/agtpool" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/storage/types" | "gitlink.org.cn/cloudream/storage/common/pkgs/storage/types" | ||||
| ) | ) | ||||
| @@ -41,12 +41,12 @@ func (o *ShardRead) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| Debugf("reading from shard store") | Debugf("reading from shard store") | ||||
| defer logger.Debugf("reading from shard store finished") | defer logger.Debugf("reading from shard store finished") | ||||
| stgMgr, err := exec.GetValueByType[*svcmgr.Manager](ctx) | |||||
| stgAgts, err := exec.GetValueByType[*agtpool.AgentPool](ctx) | |||||
| if err != nil { | if err != nil { | ||||
| return fmt.Errorf("getting storage manager: %w", err) | return fmt.Errorf("getting storage manager: %w", err) | ||||
| } | } | ||||
| store, err := stgMgr.GetShardStore(o.StorageID) | |||||
| store, err := stgAgts.GetShardStore(o.StorageID) | |||||
| if err != nil { | if err != nil { | ||||
| return fmt.Errorf("getting shard store of storage %v: %w", o.StorageID, err) | return fmt.Errorf("getting shard store of storage %v: %w", o.StorageID, err) | ||||
| } | } | ||||
| @@ -83,12 +83,12 @@ func (o *ShardWrite) Execute(ctx *exec.ExecContext, e *exec.Executor) error { | |||||
| Debugf("writting file to shard store") | Debugf("writting file to shard store") | ||||
| defer logger.Debugf("write to shard store finished") | defer logger.Debugf("write to shard store finished") | ||||
| stgMgr, err := exec.GetValueByType[*svcmgr.Manager](ctx) | |||||
| stgAgts, err := exec.GetValueByType[*agtpool.AgentPool](ctx) | |||||
| if err != nil { | if err != nil { | ||||
| return fmt.Errorf("getting storage manager: %w", err) | return fmt.Errorf("getting storage manager: %w", err) | ||||
| } | } | ||||
| store, err := stgMgr.GetShardStore(o.StorageID) | |||||
| store, err := stgAgts.GetShardStore(o.StorageID) | |||||
| if err != nil { | if err != nil { | ||||
| return fmt.Errorf("getting shard store of storage %v: %w", o.StorageID, err) | return fmt.Errorf("getting shard store of storage %v: %w", o.StorageID, err) | ||||
| } | } | ||||
| @@ -7,6 +7,7 @@ import ( | |||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/plan" | "gitlink.org.cn/cloudream/common/pkgs/ioswitch/plan" | ||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | ||||
| "gitlink.org.cn/cloudream/common/utils/math2" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitchlrc" | "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitchlrc" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitchlrc/ops2" | "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitchlrc/ops2" | ||||
| ) | ) | ||||
| @@ -16,7 +17,7 @@ type GenerateContext struct { | |||||
| DAG *ops2.GraphNodeBuilder | DAG *ops2.GraphNodeBuilder | ||||
| To []ioswitchlrc.To | To []ioswitchlrc.To | ||||
| ToNodes map[ioswitchlrc.To]ops2.ToNode | ToNodes map[ioswitchlrc.To]ops2.ToNode | ||||
| StreamRange exec.Range | |||||
| StreamRange math2.Range | |||||
| } | } | ||||
| // 输入一个完整文件,从这个完整文件产生任意文件块(也可再产生完整文件)。 | // 输入一个完整文件,从这个完整文件产生任意文件块(也可再产生完整文件)。 | ||||
| @@ -48,7 +49,7 @@ func Encode(fr ioswitchlrc.From, toes []ioswitchlrc.To, blder *exec.PlanBuilder) | |||||
| generateClone(&ctx) | generateClone(&ctx) | ||||
| generateRange(&ctx) | generateRange(&ctx) | ||||
| return plan.Generate(ctx.DAG.Graph, blder) | |||||
| return plan.Compile(ctx.DAG.Graph, blder) | |||||
| } | } | ||||
| func buildDAGEncode(ctx *GenerateContext, fr ioswitchlrc.From, toes []ioswitchlrc.To) error { | func buildDAGEncode(ctx *GenerateContext, fr ioswitchlrc.From, toes []ioswitchlrc.To) error { | ||||
| @@ -145,7 +146,7 @@ func ReconstructAny(frs []ioswitchlrc.From, toes []ioswitchlrc.To, blder *exec.P | |||||
| generateClone(&ctx) | generateClone(&ctx) | ||||
| generateRange(&ctx) | generateRange(&ctx) | ||||
| return plan.Generate(ctx.DAG.Graph, blder) | |||||
| return plan.Compile(ctx.DAG.Graph, blder) | |||||
| } | } | ||||
| func buildDAGReconstructAny(ctx *GenerateContext, frs []ioswitchlrc.From, toes []ioswitchlrc.To) error { | func buildDAGReconstructAny(ctx *GenerateContext, frs []ioswitchlrc.From, toes []ioswitchlrc.To) error { | ||||
| @@ -266,7 +267,7 @@ func ReconstructGroup(frs []ioswitchlrc.From, toes []ioswitchlrc.To, blder *exec | |||||
| generateClone(&ctx) | generateClone(&ctx) | ||||
| generateRange(&ctx) | generateRange(&ctx) | ||||
| return plan.Generate(ctx.DAG.Graph, blder) | |||||
| return plan.Compile(ctx.DAG.Graph, blder) | |||||
| } | } | ||||
| func buildDAGReconstructGroup(ctx *GenerateContext, frs []ioswitchlrc.From, toes []ioswitchlrc.To) error { | func buildDAGReconstructGroup(ctx *GenerateContext, frs []ioswitchlrc.From, toes []ioswitchlrc.To) error { | ||||
| @@ -5,7 +5,6 @@ import ( | |||||
| "math" | "math" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/dag" | "gitlink.org.cn/cloudream/common/pkgs/ioswitch/dag" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec" | |||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | ||||
| "gitlink.org.cn/cloudream/common/utils/math2" | "gitlink.org.cn/cloudream/common/utils/math2" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitchlrc" | "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitchlrc" | ||||
| @@ -17,7 +16,7 @@ import ( | |||||
| func calcStreamRange(ctx *GenerateContext) { | func calcStreamRange(ctx *GenerateContext) { | ||||
| stripSize := int64(ctx.LRC.ChunkSize * ctx.LRC.K) | stripSize := int64(ctx.LRC.ChunkSize * ctx.LRC.K) | ||||
| rng := exec.Range{ | |||||
| rng := math2.Range{ | |||||
| Offset: math.MaxInt64, | Offset: math.MaxInt64, | ||||
| } | } | ||||
| @@ -49,8 +48,8 @@ func calcStreamRange(ctx *GenerateContext) { | |||||
| } | } | ||||
| func buildFromNode(ctx *GenerateContext, f ioswitchlrc.From) (ops2.FromNode, error) { | func buildFromNode(ctx *GenerateContext, f ioswitchlrc.From) (ops2.FromNode, error) { | ||||
| var repRange exec.Range | |||||
| var blkRange exec.Range | |||||
| var repRange math2.Range | |||||
| var blkRange math2.Range | |||||
| repRange.Offset = ctx.StreamRange.Offset | repRange.Offset = ctx.StreamRange.Offset | ||||
| blkRange.Offset = ctx.StreamRange.Offset / int64(ctx.LRC.ChunkSize*ctx.LRC.K) * int64(ctx.LRC.ChunkSize) | blkRange.Offset = ctx.StreamRange.Offset / int64(ctx.LRC.ChunkSize*ctx.LRC.K) * int64(ctx.LRC.ChunkSize) | ||||
| @@ -234,7 +233,7 @@ func generateRange(ctx *GenerateContext) { | |||||
| n := ctx.DAG.NewRange() | n := ctx.DAG.NewRange() | ||||
| toInput := toNode.Input() | toInput := toNode.Input() | ||||
| *n.Env() = *toInput.Var().Src.Env() | *n.Env() = *toInput.Var().Src.Env() | ||||
| rnged := n.RangeStream(toInput.Var(), exec.Range{ | |||||
| rnged := n.RangeStream(toInput.Var(), math2.Range{ | |||||
| Offset: toRng.Offset - ctx.StreamRange.Offset, | Offset: toRng.Offset - ctx.StreamRange.Offset, | ||||
| Length: toRng.Length, | Length: toRng.Length, | ||||
| }) | }) | ||||
| @@ -250,7 +249,7 @@ func generateRange(ctx *GenerateContext) { | |||||
| n := ctx.DAG.NewRange() | n := ctx.DAG.NewRange() | ||||
| toInput := toNode.Input() | toInput := toNode.Input() | ||||
| *n.Env() = *toInput.Var().Src.Env() | *n.Env() = *toInput.Var().Src.Env() | ||||
| rnged := n.RangeStream(toInput.Var(), exec.Range{ | |||||
| rnged := n.RangeStream(toInput.Var(), math2.Range{ | |||||
| Offset: toRng.Offset - blkStart, | Offset: toRng.Offset - blkStart, | ||||
| Length: toRng.Length, | Length: toRng.Length, | ||||
| }) | }) | ||||
| @@ -0,0 +1,96 @@ | |||||
| package metacache | |||||
| import ( | |||||
| "sync" | |||||
| "time" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/logger" | |||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | |||||
| stgglb "gitlink.org.cn/cloudream/storage/common/globals" | |||||
| coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator" | |||||
| ) | |||||
| func (m *MetaCacheHost) AddConnectivity() *Connectivity { | |||||
| cache := &Connectivity{ | |||||
| entries: make(map[cdssdk.HubID]*ConnectivityEntry), | |||||
| } | |||||
| m.caches = append(m.caches, cache) | |||||
| return cache | |||||
| } | |||||
| type Connectivity struct { | |||||
| lock sync.RWMutex | |||||
| entries map[cdssdk.HubID]*ConnectivityEntry | |||||
| } | |||||
| func (c *Connectivity) Get(from cdssdk.HubID, to cdssdk.HubID) *time.Duration { | |||||
| for i := 0; i < 2; i++ { | |||||
| c.lock.RLock() | |||||
| entry, ok := c.entries[from] | |||||
| if ok { | |||||
| con, ok := entry.To[to] | |||||
| if ok { | |||||
| c.lock.RUnlock() | |||||
| if con.Latency == nil { | |||||
| return nil | |||||
| } | |||||
| l := time.Millisecond * time.Duration(*con.Latency) | |||||
| return &l | |||||
| } | |||||
| } | |||||
| c.lock.RUnlock() | |||||
| c.load(from) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func (c *Connectivity) ClearOutdated() { | |||||
| c.lock.Lock() | |||||
| defer c.lock.Unlock() | |||||
| for hubID, entry := range c.entries { | |||||
| if time.Since(entry.UpdateTime) > time.Minute*5 { | |||||
| delete(c.entries, hubID) | |||||
| } | |||||
| } | |||||
| } | |||||
| func (c *Connectivity) load(hubID cdssdk.HubID) { | |||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | |||||
| if err != nil { | |||||
| logger.Warnf("new coordinator client: %v", err) | |||||
| return | |||||
| } | |||||
| defer stgglb.CoordinatorMQPool.Release(coorCli) | |||||
| get, err := coorCli.GetHubConnectivities(coormq.ReqGetHubConnectivities([]cdssdk.HubID{hubID})) | |||||
| if err != nil { | |||||
| logger.Warnf("get hub connectivities: %v", err) | |||||
| return | |||||
| } | |||||
| c.lock.Lock() | |||||
| defer c.lock.Unlock() | |||||
| ce := &ConnectivityEntry{ | |||||
| From: hubID, | |||||
| To: make(map[cdssdk.HubID]cdssdk.HubConnectivity), | |||||
| UpdateTime: time.Now(), | |||||
| } | |||||
| for _, conn := range get.Connectivities { | |||||
| ce.To[conn.ToHubID] = conn | |||||
| } | |||||
| c.entries[hubID] = ce | |||||
| } | |||||
| type ConnectivityEntry struct { | |||||
| From cdssdk.HubID | |||||
| To map[cdssdk.HubID]cdssdk.HubConnectivity | |||||
| UpdateTime time.Time | |||||
| } | |||||
| @@ -0,0 +1,27 @@ | |||||
| package metacache | |||||
| import "time" | |||||
| type MetaCache interface { | |||||
| ClearOutdated() | |||||
| } | |||||
| type MetaCacheHost struct { | |||||
| caches []MetaCache | |||||
| } | |||||
| func NewHost() *MetaCacheHost { | |||||
| return &MetaCacheHost{} | |||||
| } | |||||
| func (m *MetaCacheHost) Serve() { | |||||
| ticker := time.NewTicker(time.Minute) | |||||
| for { | |||||
| select { | |||||
| case <-ticker.C: | |||||
| for _, cache := range m.caches { | |||||
| cache.ClearOutdated() | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,75 @@ | |||||
| package metacache | |||||
| import ( | |||||
| "time" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/logger" | |||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | |||||
| stgglb "gitlink.org.cn/cloudream/storage/common/globals" | |||||
| coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator" | |||||
| ) | |||||
| func (m *MetaCacheHost) AddHubMeta() *HubMeta { | |||||
| meta := &HubMeta{} | |||||
| meta.cache = NewSimpleMetaCache(SimpleMetaCacheConfig[cdssdk.HubID, cdssdk.Hub]{ | |||||
| Getter: meta.load, | |||||
| Expire: time.Minute * 5, | |||||
| }) | |||||
| m.caches = append(m.caches, meta) | |||||
| return meta | |||||
| } | |||||
| type HubMeta struct { | |||||
| cache *SimpleMetaCache[cdssdk.HubID, cdssdk.Hub] | |||||
| } | |||||
| func (h *HubMeta) Get(hubID cdssdk.HubID) *cdssdk.Hub { | |||||
| v, ok := h.cache.Get(hubID) | |||||
| if ok { | |||||
| return &v | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func (h *HubMeta) GetMany(hubIDs []cdssdk.HubID) []*cdssdk.Hub { | |||||
| vs, oks := h.cache.GetMany(hubIDs) | |||||
| ret := make([]*cdssdk.Hub, len(vs)) | |||||
| for i := range vs { | |||||
| if oks[i] { | |||||
| ret[i] = &vs[i] | |||||
| } | |||||
| } | |||||
| return ret | |||||
| } | |||||
| func (h *HubMeta) ClearOutdated() { | |||||
| h.cache.ClearOutdated() | |||||
| } | |||||
| func (h *HubMeta) load(keys []cdssdk.HubID) ([]cdssdk.Hub, []bool) { | |||||
| vs := make([]cdssdk.Hub, len(keys)) | |||||
| oks := make([]bool, len(keys)) | |||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | |||||
| if err != nil { | |||||
| logger.Warnf("new coordinator client: %v", err) | |||||
| return vs, oks | |||||
| } | |||||
| defer stgglb.CoordinatorMQPool.Release(coorCli) | |||||
| get, err := coorCli.GetHubs(coormq.NewGetHubs(keys)) | |||||
| if err != nil { | |||||
| logger.Warnf("get hubs: %v", err) | |||||
| return vs, oks | |||||
| } | |||||
| for i := range keys { | |||||
| if get.Hubs[i] != nil { | |||||
| vs[i] = *get.Hubs[i] | |||||
| oks[i] = true | |||||
| } | |||||
| } | |||||
| return vs, oks | |||||
| } | |||||
| @@ -0,0 +1,121 @@ | |||||
| package metacache | |||||
| import ( | |||||
| "sync" | |||||
| "time" | |||||
| ) | |||||
| type SimpleMetaCacheConfig[K comparable, V any] struct { | |||||
| Getter Getter[K, V] | |||||
| Expire time.Duration | |||||
| } | |||||
| type Getter[K comparable, V any] func(keys []K) ([]V, []bool) | |||||
| type SimpleMetaCache[K comparable, V any] struct { | |||||
| lock sync.RWMutex | |||||
| cache map[K]*CacheEntry[K, V] | |||||
| cfg SimpleMetaCacheConfig[K, V] | |||||
| } | |||||
| func NewSimpleMetaCache[K comparable, V any](cfg SimpleMetaCacheConfig[K, V]) *SimpleMetaCache[K, V] { | |||||
| return &SimpleMetaCache[K, V]{ | |||||
| cache: make(map[K]*CacheEntry[K, V]), | |||||
| cfg: cfg, | |||||
| } | |||||
| } | |||||
| func (mc *SimpleMetaCache[K, V]) Get(key K) (V, bool) { | |||||
| var ret V | |||||
| var ok bool | |||||
| for i := 0; i < 2; i++ { | |||||
| mc.lock.RLock() | |||||
| entry, o := mc.cache[key] | |||||
| if o { | |||||
| ret = entry.Data | |||||
| ok = true | |||||
| } | |||||
| mc.lock.RUnlock() | |||||
| if o { | |||||
| break | |||||
| } | |||||
| mc.load([]K{key}) | |||||
| } | |||||
| return ret, ok | |||||
| } | |||||
| func (mc *SimpleMetaCache[K, V]) GetMany(keys []K) ([]V, []bool) { | |||||
| result := make([]V, len(keys)) | |||||
| oks := make([]bool, len(keys)) | |||||
| for i := 0; i < 2; i++ { | |||||
| allGet := true | |||||
| mc.lock.RLock() | |||||
| for i, key := range keys { | |||||
| entry, ok := mc.cache[key] | |||||
| if ok { | |||||
| result[i] = entry.Data | |||||
| oks[i] = true | |||||
| } else { | |||||
| allGet = false | |||||
| } | |||||
| } | |||||
| mc.lock.RUnlock() | |||||
| if allGet { | |||||
| break | |||||
| } | |||||
| mc.load(keys) | |||||
| } | |||||
| return result, oks | |||||
| } | |||||
| func (mc *SimpleMetaCache[K, V]) load(keys []K) { | |||||
| vs, getOks := mc.cfg.Getter(keys) | |||||
| mc.lock.Lock() | |||||
| defer mc.lock.Unlock() | |||||
| for i, key := range keys { | |||||
| if !getOks[i] { | |||||
| continue | |||||
| } | |||||
| _, ok := mc.cache[key] | |||||
| // 缓存中已有key则认为缓存中是最新的,不再更新 | |||||
| if ok { | |||||
| continue | |||||
| } | |||||
| entry := &CacheEntry[K, V]{ | |||||
| Key: key, | |||||
| Data: vs[i], | |||||
| UpdateTime: time.Now(), | |||||
| } | |||||
| mc.cache[key] = entry | |||||
| } | |||||
| } | |||||
| func (mc *SimpleMetaCache[K, V]) ClearOutdated() { | |||||
| mc.lock.Lock() | |||||
| defer mc.lock.Unlock() | |||||
| for key, entry := range mc.cache { | |||||
| dt := time.Since(entry.UpdateTime) | |||||
| if dt > mc.cfg.Expire || dt < 0 { | |||||
| delete(mc.cache, key) | |||||
| } | |||||
| } | |||||
| } | |||||
| type CacheEntry[K comparable, V any] struct { | |||||
| Key K | |||||
| Data V | |||||
| UpdateTime time.Time | |||||
| } | |||||
| @@ -0,0 +1,76 @@ | |||||
| package metacache | |||||
| import ( | |||||
| "time" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/logger" | |||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | |||||
| stgglb "gitlink.org.cn/cloudream/storage/common/globals" | |||||
| stgmod "gitlink.org.cn/cloudream/storage/common/models" | |||||
| coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator" | |||||
| ) | |||||
| func (m *MetaCacheHost) AddStorageMeta() *StorageMeta { | |||||
| meta := &StorageMeta{} | |||||
| meta.cache = NewSimpleMetaCache(SimpleMetaCacheConfig[cdssdk.StorageID, stgmod.StorageDetail]{ | |||||
| Getter: meta.load, | |||||
| Expire: time.Minute * 5, | |||||
| }) | |||||
| m.caches = append(m.caches, meta) | |||||
| return meta | |||||
| } | |||||
| type StorageMeta struct { | |||||
| cache *SimpleMetaCache[cdssdk.StorageID, stgmod.StorageDetail] | |||||
| } | |||||
| func (s *StorageMeta) Get(stgID cdssdk.StorageID) *stgmod.StorageDetail { | |||||
| v, ok := s.cache.Get(stgID) | |||||
| if ok { | |||||
| return &v | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func (s *StorageMeta) GetMany(stgIDs []cdssdk.StorageID) []*stgmod.StorageDetail { | |||||
| vs, oks := s.cache.GetMany(stgIDs) | |||||
| ret := make([]*stgmod.StorageDetail, len(vs)) | |||||
| for i := range vs { | |||||
| if oks[i] { | |||||
| ret[i] = &vs[i] | |||||
| } | |||||
| } | |||||
| return ret | |||||
| } | |||||
| func (s *StorageMeta) ClearOutdated() { | |||||
| s.cache.ClearOutdated() | |||||
| } | |||||
| func (s *StorageMeta) load(keys []cdssdk.StorageID) ([]stgmod.StorageDetail, []bool) { | |||||
| vs := make([]stgmod.StorageDetail, len(keys)) | |||||
| oks := make([]bool, len(keys)) | |||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | |||||
| if err != nil { | |||||
| logger.Warnf("new coordinator client: %v", err) | |||||
| return vs, oks | |||||
| } | |||||
| defer stgglb.CoordinatorMQPool.Release(coorCli) | |||||
| get, err := coorCli.GetStorageDetails(coormq.ReqGetStorageDetails(keys)) | |||||
| if err != nil { | |||||
| logger.Warnf("get storage details: %v", err) | |||||
| return vs, oks | |||||
| } | |||||
| for i := range keys { | |||||
| if get.Storages[i] != nil { | |||||
| vs[i] = *get.Storages[i] | |||||
| oks[i] = true | |||||
| } | |||||
| } | |||||
| return vs, oks | |||||
| } | |||||
| @@ -13,8 +13,8 @@ type Client struct { | |||||
| id cdssdk.HubID | id cdssdk.HubID | ||||
| } | } | ||||
| func NewClient(id cdssdk.HubID, cfg *stgmq.Config) (*Client, error) { | |||||
| rabbitCli, err := mq.NewRabbitMQTransport(cfg.MakeConnectingURL(), stgmq.MakeAgentQueueName(int64(id)), "") | |||||
| func NewClient(id cdssdk.HubID, cfg mq.Config) (*Client, error) { | |||||
| rabbitCli, err := mq.NewRabbitMQTransport(cfg, stgmq.MakeAgentQueueName(int64(id)), "") | |||||
| if err != nil { | if err != nil { | ||||
| return nil, err | return nil, err | ||||
| } | } | ||||
| @@ -35,12 +35,12 @@ type Pool interface { | |||||
| } | } | ||||
| type pool struct { | type pool struct { | ||||
| mqcfg *stgmq.Config | |||||
| mqcfg mq.Config | |||||
| shareds map[cdssdk.HubID]*Client | shareds map[cdssdk.HubID]*Client | ||||
| lock sync.Mutex | lock sync.Mutex | ||||
| } | } | ||||
| func NewPool(mqcfg *stgmq.Config) Pool { | |||||
| func NewPool(mqcfg mq.Config) Pool { | |||||
| return &pool{ | return &pool{ | ||||
| mqcfg: mqcfg, | mqcfg: mqcfg, | ||||
| shareds: make(map[cdssdk.HubID]*Client), | shareds: make(map[cdssdk.HubID]*Client), | ||||
| @@ -20,18 +20,17 @@ type Server struct { | |||||
| rabbitSvr mq.RabbitMQServer | rabbitSvr mq.RabbitMQServer | ||||
| } | } | ||||
| func NewServer(svc Service, id cdssdk.HubID, cfg *mymq.Config) (*Server, error) { | |||||
| func NewServer(svc Service, id cdssdk.HubID, cfg mq.Config) (*Server, error) { | |||||
| srv := &Server{ | srv := &Server{ | ||||
| service: svc, | service: svc, | ||||
| } | } | ||||
| rabbitSvr, err := mq.NewRabbitMQServer( | rabbitSvr, err := mq.NewRabbitMQServer( | ||||
| cfg.MakeConnectingURL(), | |||||
| cfg, | |||||
| mymq.MakeAgentQueueName(int64(id)), | mymq.MakeAgentQueueName(int64(id)), | ||||
| func(msg *mq.Message) (*mq.Message, error) { | func(msg *mq.Message) (*mq.Message, error) { | ||||
| return msgDispatcher.Handle(srv.service, msg) | return msgDispatcher.Handle(srv.service, msg) | ||||
| }, | }, | ||||
| cfg.Param, | |||||
| ) | ) | ||||
| if err != nil { | if err != nil { | ||||
| return nil, err | return nil, err | ||||
| @@ -3,142 +3,14 @@ package agent | |||||
| import ( | import ( | ||||
| "gitlink.org.cn/cloudream/common/pkgs/mq" | "gitlink.org.cn/cloudream/common/pkgs/mq" | ||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | ||||
| stgmod "gitlink.org.cn/cloudream/storage/common/models" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/db2/model" | |||||
| ) | ) | ||||
| type StorageService interface { | type StorageService interface { | ||||
| StartStorageLoadPackage(msg *StartStorageLoadPackage) (*StartStorageLoadPackageResp, *mq.CodeMessage) | |||||
| WaitStorageLoadPackage(msg *WaitStorageLoadPackage) (*WaitStorageLoadPackageResp, *mq.CodeMessage) | |||||
| StorageCheck(msg *StorageCheck) (*StorageCheckResp, *mq.CodeMessage) | |||||
| StorageGC(msg *StorageGC) (*StorageGCResp, *mq.CodeMessage) | |||||
| StartStorageCreatePackage(msg *StartStorageCreatePackage) (*StartStorageCreatePackageResp, *mq.CodeMessage) | StartStorageCreatePackage(msg *StartStorageCreatePackage) (*StartStorageCreatePackageResp, *mq.CodeMessage) | ||||
| WaitStorageCreatePackage(msg *WaitStorageCreatePackage) (*WaitStorageCreatePackageResp, *mq.CodeMessage) | WaitStorageCreatePackage(msg *WaitStorageCreatePackage) (*WaitStorageCreatePackageResp, *mq.CodeMessage) | ||||
| } | } | ||||
| // 启动调度Package的任务 | |||||
| var _ = Register(Service.StartStorageLoadPackage) | |||||
| type StartStorageLoadPackage struct { | |||||
| mq.MessageBodyBase | |||||
| UserID cdssdk.UserID `json:"userID"` | |||||
| PackageID cdssdk.PackageID `json:"packageID"` | |||||
| StorageID cdssdk.StorageID `json:"storageID"` | |||||
| } | |||||
| type StartStorageLoadPackageResp struct { | |||||
| mq.MessageBodyBase | |||||
| TaskID string `json:"taskID"` | |||||
| } | |||||
| func NewStartStorageLoadPackage(userID cdssdk.UserID, packageID cdssdk.PackageID, storageID cdssdk.StorageID) *StartStorageLoadPackage { | |||||
| return &StartStorageLoadPackage{ | |||||
| UserID: userID, | |||||
| PackageID: packageID, | |||||
| StorageID: storageID, | |||||
| } | |||||
| } | |||||
| func NewStartStorageLoadPackageResp(taskID string) *StartStorageLoadPackageResp { | |||||
| return &StartStorageLoadPackageResp{ | |||||
| TaskID: taskID, | |||||
| } | |||||
| } | |||||
| func (client *Client) StartStorageLoadPackage(msg *StartStorageLoadPackage, opts ...mq.RequestOption) (*StartStorageLoadPackageResp, error) { | |||||
| return mq.Request(Service.StartStorageLoadPackage, client.rabbitCli, msg, opts...) | |||||
| } | |||||
| // 等待调度Package的任务 | |||||
| var _ = Register(Service.WaitStorageLoadPackage) | |||||
| type WaitStorageLoadPackage struct { | |||||
| mq.MessageBodyBase | |||||
| TaskID string `json:"taskID"` | |||||
| WaitTimeoutMs int64 `json:"waitTimeout"` | |||||
| } | |||||
| type WaitStorageLoadPackageResp struct { | |||||
| mq.MessageBodyBase | |||||
| IsComplete bool `json:"isComplete"` | |||||
| Error string `json:"error"` | |||||
| PackagePath string `json:"packagePath"` // 加载后的Package的路径,相对于数据库中配置的Directory | |||||
| LocalBase string `json:"localBase"` // 存储服务本地的目录,LocalBase + PackagePath = Package在代理节点上的完整路径 | |||||
| RemoteBase string `json:"remoteBase"` // 存储服务远程的目录,RemoteBase + PackagePath = Package在存储服务中的完整路径 | |||||
| } | |||||
| func NewWaitStorageLoadPackage(taskID string, waitTimeoutMs int64) *WaitStorageLoadPackage { | |||||
| return &WaitStorageLoadPackage{ | |||||
| TaskID: taskID, | |||||
| WaitTimeoutMs: waitTimeoutMs, | |||||
| } | |||||
| } | |||||
| func NewWaitStorageLoadPackageResp(isComplete bool, err string, packagePath string, localBase string, remoteBase string) *WaitStorageLoadPackageResp { | |||||
| return &WaitStorageLoadPackageResp{ | |||||
| IsComplete: isComplete, | |||||
| Error: err, | |||||
| PackagePath: packagePath, | |||||
| LocalBase: localBase, | |||||
| RemoteBase: remoteBase, | |||||
| } | |||||
| } | |||||
| func (client *Client) WaitStorageLoadPackage(msg *WaitStorageLoadPackage, opts ...mq.RequestOption) (*WaitStorageLoadPackageResp, error) { | |||||
| return mq.Request(Service.WaitStorageLoadPackage, client.rabbitCli, msg, opts...) | |||||
| } | |||||
| // 检查Storage | |||||
| var _ = Register(Service.StorageCheck) | |||||
| type StorageCheck struct { | |||||
| mq.MessageBodyBase | |||||
| StorageID cdssdk.StorageID `json:"storageID"` | |||||
| } | |||||
| type StorageCheckResp struct { | |||||
| mq.MessageBodyBase | |||||
| Packages []stgmod.LoadedPackageID `json:"packages"` | |||||
| } | |||||
| func NewStorageCheck(storageID cdssdk.StorageID) *StorageCheck { | |||||
| return &StorageCheck{ | |||||
| StorageID: storageID, | |||||
| } | |||||
| } | |||||
| func NewStorageCheckResp(packages []stgmod.LoadedPackageID) *StorageCheckResp { | |||||
| return &StorageCheckResp{ | |||||
| Packages: packages, | |||||
| } | |||||
| } | |||||
| func (client *Client) StorageCheck(msg *StorageCheck, opts ...mq.RequestOption) (*StorageCheckResp, error) { | |||||
| return mq.Request(Service.StorageCheck, client.rabbitCli, msg, opts...) | |||||
| } | |||||
| // 清理Cache中不用的文件 | |||||
| var _ = Register(Service.StorageGC) | |||||
| type StorageGC struct { | |||||
| mq.MessageBodyBase | |||||
| StorageID cdssdk.StorageID `json:"storageID"` | |||||
| Packages []model.StoragePackage `json:"packages"` | |||||
| } | |||||
| type StorageGCResp struct { | |||||
| mq.MessageBodyBase | |||||
| } | |||||
| func ReqStorageGC(storageID cdssdk.StorageID, packages []model.StoragePackage) *StorageGC { | |||||
| return &StorageGC{ | |||||
| StorageID: storageID, | |||||
| Packages: packages, | |||||
| } | |||||
| } | |||||
| func RespStorageGC() *StorageGCResp { | |||||
| return &StorageGCResp{} | |||||
| } | |||||
| func (client *Client) StorageGC(msg *StorageGC, opts ...mq.RequestOption) (*StorageGCResp, error) { | |||||
| return mq.Request(Service.StorageGC, client.rabbitCli, msg, opts...) | |||||
| } | |||||
| // 启动从Storage上传Package的任务 | // 启动从Storage上传Package的任务 | ||||
| var _ = Register(Service.StartStorageCreatePackage) | var _ = Register(Service.StartStorageCreatePackage) | ||||
| @@ -1,19 +0,0 @@ | |||||
| package mq | |||||
| import ( | |||||
| "fmt" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/mq" | |||||
| ) | |||||
| type Config struct { | |||||
| Address string `json:"address"` | |||||
| Account string `json:"account"` | |||||
| Password string `json:"password"` | |||||
| VHost string `json:"vhost"` | |||||
| Param mq.RabbitMQParam `json:"param"` | |||||
| } | |||||
| func (cfg *Config) MakeConnectingURL() string { | |||||
| return fmt.Sprintf("amqp://%s:%s@%s%s", cfg.Account, cfg.Password, cfg.Address, cfg.VHost) | |||||
| } | |||||
| @@ -11,8 +11,8 @@ type Client struct { | |||||
| rabbitCli *mq.RabbitMQTransport | rabbitCli *mq.RabbitMQTransport | ||||
| } | } | ||||
| func NewClient(cfg *stgmq.Config) (*Client, error) { | |||||
| rabbitCli, err := mq.NewRabbitMQTransport(cfg.MakeConnectingURL(), stgmq.COORDINATOR_QUEUE_NAME, "") | |||||
| func NewClient(cfg mq.Config) (*Client, error) { | |||||
| rabbitCli, err := mq.NewRabbitMQTransport(cfg, stgmq.COORDINATOR_QUEUE_NAME, "") | |||||
| if err != nil { | if err != nil { | ||||
| return nil, err | return nil, err | ||||
| } | } | ||||
| @@ -32,12 +32,12 @@ type Pool interface { | |||||
| } | } | ||||
| type pool struct { | type pool struct { | ||||
| mqcfg *stgmq.Config | |||||
| mqcfg mq.Config | |||||
| shared *Client | shared *Client | ||||
| lock sync.Mutex | lock sync.Mutex | ||||
| } | } | ||||
| func NewPool(mqcfg *stgmq.Config) Pool { | |||||
| func NewPool(mqcfg mq.Config) Pool { | |||||
| return &pool{ | return &pool{ | ||||
| mqcfg: mqcfg, | mqcfg: mqcfg, | ||||
| } | } | ||||
| @@ -80,7 +80,7 @@ type GetHubs struct { | |||||
| } | } | ||||
| type GetHubsResp struct { | type GetHubsResp struct { | ||||
| mq.MessageBodyBase | mq.MessageBodyBase | ||||
| Hubs []cdssdk.Hub `json:"hubs"` | |||||
| Hubs []*cdssdk.Hub `json:"hubs"` | |||||
| } | } | ||||
| func NewGetHubs(hubIDs []cdssdk.HubID) *GetHubs { | func NewGetHubs(hubIDs []cdssdk.HubID) *GetHubs { | ||||
| @@ -88,7 +88,7 @@ func NewGetHubs(hubIDs []cdssdk.HubID) *GetHubs { | |||||
| HubIDs: hubIDs, | HubIDs: hubIDs, | ||||
| } | } | ||||
| } | } | ||||
| func NewGetHubsResp(hubs []cdssdk.Hub) *GetHubsResp { | |||||
| func NewGetHubsResp(hubs []*cdssdk.Hub) *GetHubsResp { | |||||
| return &GetHubsResp{ | return &GetHubsResp{ | ||||
| Hubs: hubs, | Hubs: hubs, | ||||
| } | } | ||||
| @@ -96,7 +96,7 @@ func NewGetHubsResp(hubs []cdssdk.Hub) *GetHubsResp { | |||||
| func (r *GetHubsResp) GetHub(id cdssdk.HubID) *cdssdk.Hub { | func (r *GetHubsResp) GetHub(id cdssdk.HubID) *cdssdk.Hub { | ||||
| for _, n := range r.Hubs { | for _, n := range r.Hubs { | ||||
| if n.HubID == id { | if n.HubID == id { | ||||
| return &n | |||||
| return n | |||||
| } | } | ||||
| } | } | ||||
| @@ -10,6 +10,8 @@ import ( | |||||
| ) | ) | ||||
| type ObjectService interface { | type ObjectService interface { | ||||
| GetObjects(msg *GetObjects) (*GetObjectsResp, *mq.CodeMessage) | |||||
| GetObjectsByPath(msg *GetObjectsByPath) (*GetObjectsByPathResp, *mq.CodeMessage) | GetObjectsByPath(msg *GetObjectsByPath) (*GetObjectsByPathResp, *mq.CodeMessage) | ||||
| GetPackageObjects(msg *GetPackageObjects) (*GetPackageObjectsResp, *mq.CodeMessage) | GetPackageObjects(msg *GetPackageObjects) (*GetPackageObjectsResp, *mq.CodeMessage) | ||||
| @@ -26,11 +28,40 @@ type ObjectService interface { | |||||
| DeleteObjects(msg *DeleteObjects) (*DeleteObjectsResp, *mq.CodeMessage) | DeleteObjects(msg *DeleteObjects) (*DeleteObjectsResp, *mq.CodeMessage) | ||||
| CloneObjects(msg *CloneObjects) (*CloneObjectsResp, *mq.CodeMessage) | |||||
| GetDatabaseAll(msg *GetDatabaseAll) (*GetDatabaseAllResp, *mq.CodeMessage) | GetDatabaseAll(msg *GetDatabaseAll) (*GetDatabaseAllResp, *mq.CodeMessage) | ||||
| AddAccessStat(msg *AddAccessStat) | AddAccessStat(msg *AddAccessStat) | ||||
| } | } | ||||
| var _ = Register(Service.GetObjects) | |||||
| type GetObjects struct { | |||||
| mq.MessageBodyBase | |||||
| UserID cdssdk.UserID `json:"userID"` | |||||
| ObjectIDs []cdssdk.ObjectID `json:"objectIDs"` | |||||
| } | |||||
| type GetObjectsResp struct { | |||||
| mq.MessageBodyBase | |||||
| Objects []*cdssdk.Object `json:"objects"` | |||||
| } | |||||
| func ReqGetObjects(userID cdssdk.UserID, objectIDs []cdssdk.ObjectID) *GetObjects { | |||||
| return &GetObjects{ | |||||
| UserID: userID, | |||||
| ObjectIDs: objectIDs, | |||||
| } | |||||
| } | |||||
| func RespGetObjects(objects []*cdssdk.Object) *GetObjectsResp { | |||||
| return &GetObjectsResp{ | |||||
| Objects: objects, | |||||
| } | |||||
| } | |||||
| func (client *Client) GetObjects(msg *GetObjects) (*GetObjectsResp, error) { | |||||
| return mq.Request(Service.GetObjects, client.rabbitCli, msg) | |||||
| } | |||||
| // 查询指定前缀的Object,返回的Objects会按照ObjectID升序 | // 查询指定前缀的Object,返回的Objects会按照ObjectID升序 | ||||
| var _ = Register(Service.GetObjectsByPath) | var _ = Register(Service.GetObjectsByPath) | ||||
| @@ -256,6 +287,34 @@ func (client *Client) DeleteObjects(msg *DeleteObjects) (*DeleteObjectsResp, err | |||||
| return mq.Request(Service.DeleteObjects, client.rabbitCli, msg) | return mq.Request(Service.DeleteObjects, client.rabbitCli, msg) | ||||
| } | } | ||||
| // 克隆Object | |||||
| var _ = Register(Service.CloneObjects) | |||||
| type CloneObjects struct { | |||||
| mq.MessageBodyBase | |||||
| UserID cdssdk.UserID `json:"userID"` | |||||
| Clonings []cdsapi.CloningObject `json:"clonings"` | |||||
| } | |||||
| type CloneObjectsResp struct { | |||||
| mq.MessageBodyBase | |||||
| Objects []*cdssdk.Object `json:"objects"` | |||||
| } | |||||
| func ReqCloneObjects(userID cdssdk.UserID, clonings []cdsapi.CloningObject) *CloneObjects { | |||||
| return &CloneObjects{ | |||||
| UserID: userID, | |||||
| Clonings: clonings, | |||||
| } | |||||
| } | |||||
| func RespCloneObjects(objects []*cdssdk.Object) *CloneObjectsResp { | |||||
| return &CloneObjectsResp{ | |||||
| Objects: objects, | |||||
| } | |||||
| } | |||||
| func (client *Client) CloneObjects(msg *CloneObjects) (*CloneObjectsResp, error) { | |||||
| return mq.Request(Service.CloneObjects, client.rabbitCli, msg) | |||||
| } | |||||
| // 增加访问计数 | // 增加访问计数 | ||||
| var _ = RegisterNoReply(Service.AddAccessStat) | var _ = RegisterNoReply(Service.AddAccessStat) | ||||