| @@ -37,5 +37,7 @@ func init() { | |||||
| parseScannerEventCmdTrie.MustAdd(scevt.NewCheckRepCount, myreflect.TypeNameOf[scevt.CheckRepCount]()) | parseScannerEventCmdTrie.MustAdd(scevt.NewCheckRepCount, myreflect.TypeNameOf[scevt.CheckRepCount]()) | ||||
| parseScannerEventCmdTrie.MustAdd(scevt.NewCheckPackageRedundancy, myreflect.TypeNameOf[scevt.CheckPackageRedundancy]()) | |||||
| commands.MustAdd(ScannerPostEvent, "scanner", "event") | commands.MustAdd(ScannerPostEvent, "scanner", "event") | ||||
| } | } | ||||
| @@ -11,13 +11,12 @@ import ( | |||||
| ) | ) | ||||
| type Config struct { | type Config struct { | ||||
| Local stgmodels.LocalMachineInfo `json:"local"` | |||||
| AgentGRPC agtrpc.PoolConfig `json:"agentGRPC"` | |||||
| MaxRepCount int `json:"maxRepCount"` | |||||
| Logger logger.Config `json:"logger"` | |||||
| RabbitMQ stgmq.Config `json:"rabbitMQ"` | |||||
| IPFS *ipfs.Config `json:"ipfs"` // 此字段非空代表客户端上存在ipfs daemon | |||||
| DistLock distlock.Config `json:"distlock"` | |||||
| Local stgmodels.LocalMachineInfo `json:"local"` | |||||
| AgentGRPC agtrpc.PoolConfig `json:"agentGRPC"` | |||||
| Logger logger.Config `json:"logger"` | |||||
| RabbitMQ stgmq.Config `json:"rabbitMQ"` | |||||
| IPFS *ipfs.Config `json:"ipfs"` // 此字段非空代表客户端上存在ipfs daemon | |||||
| DistLock distlock.Config `json:"distlock"` | |||||
| } | } | ||||
| var cfg Config | var cfg Config | ||||
| @@ -1,12 +1,12 @@ | |||||
| { | { | ||||
| "local": { | "local": { | ||||
| "localIP": "127.0.0.1", | "localIP": "127.0.0.1", | ||||
| "externalIP": "127.0.0.1" | |||||
| "externalIP": "127.0.0.1", | |||||
| "locationID": 1 | |||||
| }, | }, | ||||
| "agentGRPC": { | "agentGRPC": { | ||||
| "port": 5010 | "port": 5010 | ||||
| }, | }, | ||||
| "maxRepCount": 10, | |||||
| "logger": { | "logger": { | ||||
| "output": "stdout", | "output": "stdout", | ||||
| "level": "debug" | "level": "debug" | ||||
| @@ -1,5 +1,5 @@ | |||||
| { | { | ||||
| "minAvailableRepProportion": 0.8, | |||||
| "ecFileSizeThreshold": 104857600, | |||||
| "nodeUnavailableSeconds": 300, | "nodeUnavailableSeconds": 300, | ||||
| "logger": { | "logger": { | ||||
| "output": "file", | "output": "file", | ||||
| @@ -163,7 +163,7 @@ func uploadAndUpdatePackage(packageID cdssdk.PackageID, objectIter iterator.Uplo | |||||
| var uploadRets []ObjectUploadResult | var uploadRets []ObjectUploadResult | ||||
| //上传文件夹 | //上传文件夹 | ||||
| var adds []coormq.AddObjectInfo | |||||
| var adds []coormq.AddObjectEntry | |||||
| for { | for { | ||||
| objInfo, err := objectIter.MoveNext() | objInfo, err := objectIter.MoveNext() | ||||
| if err == iterator.ErrNoMoreItem { | if err == iterator.ErrNoMoreItem { | ||||
| @@ -190,7 +190,7 @@ func uploadAndUpdatePackage(packageID cdssdk.PackageID, objectIter iterator.Uplo | |||||
| return fmt.Errorf("uploading object: %w", err) | return fmt.Errorf("uploading object: %w", err) | ||||
| } | } | ||||
| adds = append(adds, coormq.NewAddObjectInfo(objInfo.Path, objInfo.Size, fileHash, uploadNode.Node.NodeID)) | |||||
| adds = append(adds, coormq.NewAddObjectEntry(objInfo.Path, objInfo.Size, fileHash, uploadNode.Node.NodeID)) | |||||
| return nil | return nil | ||||
| }() | }() | ||||
| if err != nil { | if err != nil { | ||||
| @@ -42,8 +42,8 @@ func (db *ObjectDB) Create(ctx SQLContext, packageID cdssdk.PackageID, path stri | |||||
| // 创建或者更新记录,返回值true代表是创建,false代表是更新 | // 创建或者更新记录,返回值true代表是创建,false代表是更新 | ||||
| func (db *ObjectDB) CreateOrUpdate(ctx SQLContext, packageID cdssdk.PackageID, path string, size int64, fileHash string) (cdssdk.ObjectID, bool, error) { | func (db *ObjectDB) CreateOrUpdate(ctx SQLContext, packageID cdssdk.PackageID, path string, size int64, fileHash string) (cdssdk.ObjectID, bool, error) { | ||||
| // 首次上传Object时,默认使用Rep模式,即使是在更新一个已有的Object也是如此 | |||||
| defRed := cdssdk.NewRepRedundancy() | |||||
| // 首次上传Object时,默认不启用冗余,即使是在更新一个已有的Object也是如此 | |||||
| defRed := cdssdk.NewNoneRedundancy() | |||||
| sql := "insert into Object(PackageID, Path, Size, FileHash, Redundancy) values(?,?,?,?,?) on duplicate key update Size = ?, FileHash = ?, Redundancy = ?" | sql := "insert into Object(PackageID, Path, Size, FileHash, Redundancy) values(?,?,?,?,?) on duplicate key update Size = ?, FileHash = ?, Redundancy = ?" | ||||
| @@ -94,7 +94,7 @@ func (*ObjectDB) GetPackageObjects(ctx SQLContext, packageID cdssdk.PackageID) ( | |||||
| return lo.Map(ret, func(o model.TempObject, idx int) model.Object { return o.ToObject() }), err | return lo.Map(ret, func(o model.TempObject, idx int) model.Object { return o.ToObject() }), err | ||||
| } | } | ||||
| func (db *ObjectDB) BatchAdd(ctx SQLContext, packageID cdssdk.PackageID, objs []coormq.AddObjectInfo) ([]cdssdk.ObjectID, error) { | |||||
| func (db *ObjectDB) BatchAdd(ctx SQLContext, packageID cdssdk.PackageID, objs []coormq.AddObjectEntry) ([]cdssdk.ObjectID, error) { | |||||
| objIDs := make([]cdssdk.ObjectID, 0, len(objs)) | objIDs := make([]cdssdk.ObjectID, 0, len(objs)) | ||||
| for _, obj := range objs { | for _, obj := range objs { | ||||
| // 创建对象的记录 | // 创建对象的记录 | ||||
| @@ -128,6 +128,36 @@ func (db *ObjectDB) BatchAdd(ctx SQLContext, packageID cdssdk.PackageID, objs [] | |||||
| return objIDs, nil | return objIDs, nil | ||||
| } | } | ||||
| func (db *ObjectDB) BatchUpdateRedundancy(ctx SQLContext, objs []coormq.ChangeObjectRedundancyEntry) error { | |||||
| for _, obj := range objs { | |||||
| _, err := ctx.Exec("update Object set Redundancy = ? where ObjectID = ?", obj.Redundancy, obj.ObjectID) | |||||
| if err != nil { | |||||
| return fmt.Errorf("updating object: %w", err) | |||||
| } | |||||
| // 删除原本所有的编码块记录,重新添加 | |||||
| if err = db.ObjectBlock().DeleteObjectAll(ctx, obj.ObjectID); err != nil { | |||||
| return fmt.Errorf("deleting all object block: %w", err) | |||||
| } | |||||
| for _, block := range obj.Blocks { | |||||
| // 首次上传默认使用不分块的rep模式 | |||||
| err = db.ObjectBlock().Create(ctx, obj.ObjectID, block.Index, block.NodeID, block.FileHash) | |||||
| if err != nil { | |||||
| return fmt.Errorf("creating object block: %w", err) | |||||
| } | |||||
| // 创建缓存记录 | |||||
| err = db.Cache().CreatePinned(ctx, block.FileHash, block.NodeID, 0) | |||||
| if err != nil { | |||||
| return fmt.Errorf("creating cache: %w", err) | |||||
| } | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func (*ObjectDB) BatchDelete(ctx SQLContext, ids []cdssdk.ObjectID) error { | func (*ObjectDB) BatchDelete(ctx SQLContext, ids []cdssdk.ObjectID) error { | ||||
| _, err := ctx.Exec("delete from Object where ObjectID in (?)", ids) | _, err := ctx.Exec("delete from Object where ObjectID in (?)", ids) | ||||
| return err | return err | ||||
| @@ -22,6 +22,12 @@ func (*StoragePackageLogDB) Get(ctx SQLContext, storageID cdssdk.StorageID, pack | |||||
| return ret, err | return ret, err | ||||
| } | } | ||||
| func (*StoragePackageLogDB) GetByPackageID(ctx SQLContext, packageID cdssdk.PackageID) ([]model.StoragePackageLog, error) { | |||||
| var ret []model.StoragePackageLog | |||||
| err := sqlx.Select(ctx, &ret, "select * from StoragePackageLog where PackageID = ?", packageID) | |||||
| return ret, err | |||||
| } | |||||
| func (*StoragePackageLogDB) Create(ctx SQLContext, storageID cdssdk.StorageID, packageID cdssdk.PackageID, userID cdssdk.UserID, createTime time.Time) error { | func (*StoragePackageLogDB) Create(ctx SQLContext, storageID cdssdk.StorageID, packageID cdssdk.PackageID, userID cdssdk.UserID, createTime time.Time) error { | ||||
| _, err := ctx.Exec("insert into StoragePackageLog values(?,?,?,?)", storageID, packageID, userID, createTime) | _, err := ctx.Exec("insert into StoragePackageLog values(?,?,?,?)", storageID, packageID, userID, createTime) | ||||
| return err | return err | ||||
| @@ -11,7 +11,7 @@ import ( | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch" | "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch" | ||||
| ) | ) | ||||
| type ECCompute struct { | |||||
| type ECReconstructAny struct { | |||||
| EC cdssdk.ECRedundancy `json:"ec"` | EC cdssdk.ECRedundancy `json:"ec"` | ||||
| InputIDs []ioswitch.StreamID `json:"inputIDs"` | InputIDs []ioswitch.StreamID `json:"inputIDs"` | ||||
| OutputIDs []ioswitch.StreamID `json:"outputIDs"` | OutputIDs []ioswitch.StreamID `json:"outputIDs"` | ||||
| @@ -19,7 +19,7 @@ type ECCompute struct { | |||||
| OutputBlockIndexes []int `json:"outputBlockIndexes"` | OutputBlockIndexes []int `json:"outputBlockIndexes"` | ||||
| } | } | ||||
| func (o *ECCompute) Execute(sw *ioswitch.Switch, planID ioswitch.PlanID) error { | |||||
| func (o *ECReconstructAny) Execute(sw *ioswitch.Switch, planID ioswitch.PlanID) error { | |||||
| rs, err := ec.NewRs(o.EC.K, o.EC.N, o.EC.ChunkSize) | rs, err := ec.NewRs(o.EC.K, o.EC.N, o.EC.ChunkSize) | ||||
| if err != nil { | if err != nil { | ||||
| return fmt.Errorf("new ec: %w", err) | return fmt.Errorf("new ec: %w", err) | ||||
| @@ -40,7 +40,7 @@ func (o *ECCompute) Execute(sw *ioswitch.Switch, planID ioswitch.PlanID) error { | |||||
| inputs = append(inputs, s.Stream) | inputs = append(inputs, s.Stream) | ||||
| } | } | ||||
| outputs := rs.ReconstructSome(inputs, o.InputBlockIndexes, o.OutputBlockIndexes) | |||||
| outputs := rs.ReconstructAny(inputs, o.InputBlockIndexes, o.OutputBlockIndexes) | |||||
| wg := sync.WaitGroup{} | wg := sync.WaitGroup{} | ||||
| for i, id := range o.OutputIDs { | for i, id := range o.OutputIDs { | ||||
| @@ -97,6 +97,6 @@ func (o *ECReconstruct) Execute(sw *ioswitch.Switch, planID ioswitch.PlanID) err | |||||
| } | } | ||||
| func init() { | func init() { | ||||
| OpUnion.AddT((*ECCompute)(nil)) | |||||
| OpUnion.AddT((*ECReconstructAny)(nil)) | |||||
| OpUnion.AddT((*ECReconstruct)(nil)) | OpUnion.AddT((*ECReconstruct)(nil)) | ||||
| } | } | ||||
| @@ -0,0 +1,40 @@ | |||||
| package ops | |||||
| import ( | |||||
| "context" | |||||
| "io" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/future" | |||||
| myio "gitlink.org.cn/cloudream/common/utils/io" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch" | |||||
| ) | |||||
| type Length struct { | |||||
| InputID ioswitch.StreamID `json:"inputID"` | |||||
| OutputID ioswitch.StreamID `json:"outputID"` | |||||
| Length int64 `json:"length"` | |||||
| } | |||||
| func (o *Length) Execute(sw *ioswitch.Switch, planID ioswitch.PlanID) error { | |||||
| strs, err := sw.WaitStreams(planID, o.InputID) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| defer strs[0].Stream.Close() | |||||
| fut := future.NewSetVoid() | |||||
| sw.StreamReady(planID, | |||||
| ioswitch.NewStream(o.OutputID, | |||||
| myio.AfterReadClosedOnce(myio.Length(strs[0].Stream, o.Length), func(closer io.ReadCloser) { | |||||
| fut.SetVoid() | |||||
| }), | |||||
| ), | |||||
| ) | |||||
| fut.Wait(context.TODO()) | |||||
| return nil | |||||
| } | |||||
| func init() { | |||||
| OpUnion.AddT((*Length)(nil)) | |||||
| } | |||||
| @@ -102,7 +102,7 @@ func (b *AgentStream) FileWrite(filePath string) { | |||||
| }) | }) | ||||
| } | } | ||||
| func (b *AgentPlanBuilder) ECCompute(ec cdssdk.ECRedundancy, inBlockIndexes []int, outBlockIndexes []int, streams ...*AgentStream) *MultiStream { | |||||
| func (b *AgentPlanBuilder) ECReconstructAny(ec cdssdk.ECRedundancy, inBlockIndexes []int, outBlockIndexes []int, streams ...*AgentStream) *MultiStream { | |||||
| mstr := &MultiStream{} | mstr := &MultiStream{} | ||||
| var inputStrIDs []ioswitch.StreamID | var inputStrIDs []ioswitch.StreamID | ||||
| @@ -111,7 +111,7 @@ func (b *AgentPlanBuilder) ECCompute(ec cdssdk.ECRedundancy, inBlockIndexes []in | |||||
| } | } | ||||
| var outputStrIDs []ioswitch.StreamID | var outputStrIDs []ioswitch.StreamID | ||||
| for i := 0; i < ec.N-ec.K; i++ { | |||||
| for i := 0; i < len(outBlockIndexes); i++ { | |||||
| info := b.owner.newStream() | info := b.owner.newStream() | ||||
| mstr.Streams = append(mstr.Streams, &AgentStream{ | mstr.Streams = append(mstr.Streams, &AgentStream{ | ||||
| owner: b, | owner: b, | ||||
| @@ -120,7 +120,7 @@ func (b *AgentPlanBuilder) ECCompute(ec cdssdk.ECRedundancy, inBlockIndexes []in | |||||
| outputStrIDs = append(outputStrIDs, info.ID) | outputStrIDs = append(outputStrIDs, info.ID) | ||||
| } | } | ||||
| b.ops = append(b.ops, &ops.ECCompute{ | |||||
| b.ops = append(b.ops, &ops.ECReconstructAny{ | |||||
| EC: ec, | EC: ec, | ||||
| InputIDs: inputStrIDs, | InputIDs: inputStrIDs, | ||||
| OutputIDs: outputStrIDs, | OutputIDs: outputStrIDs, | ||||
| @@ -183,6 +183,21 @@ func (b *AgentStream) ChunkedSplit(chunkSize int, streamCount int, paddingZeros | |||||
| return mstr | return mstr | ||||
| } | } | ||||
| func (s *AgentStream) Length(length int64) *AgentStream { | |||||
| agtStr := &AgentStream{ | |||||
| owner: s.owner, | |||||
| info: s.owner.owner.newStream(), | |||||
| } | |||||
| s.owner.ops = append(s.owner.ops, &ops.Length{ | |||||
| InputID: s.info.ID, | |||||
| OutputID: agtStr.info.ID, | |||||
| Length: length, | |||||
| }) | |||||
| return agtStr | |||||
| } | |||||
| func (s *AgentStream) ToExecutor() *ToExecutorStream { | func (s *AgentStream) ToExecutor() *ToExecutorStream { | ||||
| return &ToExecutorStream{ | return &ToExecutorStream{ | ||||
| info: s.info, | info: s.info, | ||||
| @@ -72,8 +72,19 @@ func (iter *DownloadObjectIterator) doMove(coorCli *coormq.Client) (*IterDownloa | |||||
| obj := iter.objectDetails[iter.currentIndex] | obj := iter.objectDetails[iter.currentIndex] | ||||
| switch red := obj.Object.Redundancy.(type) { | switch red := obj.Object.Redundancy.(type) { | ||||
| case *cdssdk.NoneRedundancy: | |||||
| reader, err := iter.downloadNoneOrRepObject(coorCli, iter.downloadCtx, obj) | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("downloading object: %w", err) | |||||
| } | |||||
| return &IterDownloadingObject{ | |||||
| Object: obj.Object, | |||||
| File: reader, | |||||
| }, nil | |||||
| case *cdssdk.RepRedundancy: | case *cdssdk.RepRedundancy: | ||||
| reader, err := iter.downloadRepObject(coorCli, iter.downloadCtx, obj, red) | |||||
| reader, err := iter.downloadNoneOrRepObject(coorCli, iter.downloadCtx, obj) | |||||
| if err != nil { | if err != nil { | ||||
| return nil, fmt.Errorf("downloading rep object: %w", err) | return nil, fmt.Errorf("downloading rep object: %w", err) | ||||
| } | } | ||||
| @@ -116,7 +127,7 @@ func (i *DownloadObjectIterator) chooseDownloadNode(entries []DownloadNodeInfo) | |||||
| return entries[rand.Intn(len(entries))] | return entries[rand.Intn(len(entries))] | ||||
| } | } | ||||
| func (iter *DownloadObjectIterator) downloadRepObject(coorCli *coormq.Client, ctx *DownloadContext, obj stgmodels.ObjectDetail, repRed *cdssdk.RepRedundancy) (io.ReadCloser, error) { | |||||
| func (iter *DownloadObjectIterator) downloadNoneOrRepObject(coorCli *coormq.Client, ctx *DownloadContext, obj stgmodels.ObjectDetail) (io.ReadCloser, error) { | |||||
| //采取直接读,优先选内网节点 | //采取直接读,优先选内网节点 | ||||
| var chosenNodes []DownloadNodeInfo | var chosenNodes []DownloadNodeInfo | ||||
| for i := range obj.Blocks { | for i := range obj.Blocks { | ||||
| @@ -38,7 +38,7 @@ func (client *Client) GetUserNodes(msg *GetUserNodes) (*GetUserNodesResp, error) | |||||
| return mq.Request(Service.GetUserNodes, client.rabbitCli, msg) | return mq.Request(Service.GetUserNodes, client.rabbitCli, msg) | ||||
| } | } | ||||
| // 获取指定节点的信息 | |||||
| // 获取指定节点的信息。如果NodeIDs为nil,则返回所有Node | |||||
| var _ = Register(Service.GetNodes) | var _ = Register(Service.GetNodes) | ||||
| type GetNodes struct { | type GetNodes struct { | ||||
| @@ -12,6 +12,8 @@ type ObjectService interface { | |||||
| GetPackageObjects(msg *GetPackageObjects) (*GetPackageObjectsResp, *mq.CodeMessage) | GetPackageObjects(msg *GetPackageObjects) (*GetPackageObjectsResp, *mq.CodeMessage) | ||||
| GetPackageObjectDetails(msg *GetPackageObjectDetails) (*GetPackageObjectDetailsResp, *mq.CodeMessage) | GetPackageObjectDetails(msg *GetPackageObjectDetails) (*GetPackageObjectDetailsResp, *mq.CodeMessage) | ||||
| ChangeObjectRedundancy(msg *ChangeObjectRedundancy) (*ChangeObjectRedundancyResp, *mq.CodeMessage) | |||||
| } | } | ||||
| // 查询Package中的所有Object,返回的Objects会按照ObjectID升序 | // 查询Package中的所有Object,返回的Objects会按照ObjectID升序 | ||||
| @@ -67,3 +69,31 @@ func NewGetPackageObjectDetailsResp(objects []stgmod.ObjectDetail) *GetPackageOb | |||||
| func (client *Client) GetPackageObjectDetails(msg *GetPackageObjectDetails) (*GetPackageObjectDetailsResp, error) { | func (client *Client) GetPackageObjectDetails(msg *GetPackageObjectDetails) (*GetPackageObjectDetailsResp, error) { | ||||
| return mq.Request(Service.GetPackageObjectDetails, client.rabbitCli, msg) | return mq.Request(Service.GetPackageObjectDetails, client.rabbitCli, msg) | ||||
| } | } | ||||
| // 更新Object的冗余方式 | |||||
| var _ = Register(Service.ChangeObjectRedundancy) | |||||
| type ChangeObjectRedundancy struct { | |||||
| mq.MessageBodyBase | |||||
| Entries []ChangeObjectRedundancyEntry `json:"entries"` | |||||
| } | |||||
| type ChangeObjectRedundancyResp struct { | |||||
| mq.MessageBodyBase | |||||
| } | |||||
| type ChangeObjectRedundancyEntry struct { | |||||
| ObjectID cdssdk.ObjectID `json:"objectID"` | |||||
| Redundancy cdssdk.Redundancy `json:"redundancy"` | |||||
| Blocks []stgmod.ObjectBlock `json:"blocks"` | |||||
| } | |||||
| func ReqChangeObjectRedundancy(entries []ChangeObjectRedundancyEntry) *ChangeObjectRedundancy { | |||||
| return &ChangeObjectRedundancy{ | |||||
| Entries: entries, | |||||
| } | |||||
| } | |||||
| func RespChangeObjectRedundancy() *ChangeObjectRedundancyResp { | |||||
| return &ChangeObjectRedundancyResp{} | |||||
| } | |||||
| func (client *Client) ChangeObjectRedundancy(msg *ChangeObjectRedundancy) (*ChangeObjectRedundancyResp, error) { | |||||
| return mq.Request(Service.ChangeObjectRedundancy, client.rabbitCli, msg) | |||||
| } | |||||
| @@ -12,7 +12,7 @@ type PackageService interface { | |||||
| CreatePackage(msg *CreatePackage) (*CreatePackageResp, *mq.CodeMessage) | CreatePackage(msg *CreatePackage) (*CreatePackageResp, *mq.CodeMessage) | ||||
| UpdateECPackage(msg *UpdatePackage) (*UpdatePackageResp, *mq.CodeMessage) | |||||
| UpdatePackage(msg *UpdatePackage) (*UpdatePackageResp, *mq.CodeMessage) | |||||
| DeletePackage(msg *DeletePackage) (*DeletePackageResp, *mq.CodeMessage) | DeletePackage(msg *DeletePackage) (*DeletePackageResp, *mq.CodeMessage) | ||||
| @@ -80,25 +80,25 @@ func (client *Client) CreatePackage(msg *CreatePackage) (*CreatePackageResp, err | |||||
| } | } | ||||
| // 更新EC备份模式的Package | // 更新EC备份模式的Package | ||||
| var _ = Register(Service.UpdateECPackage) | |||||
| var _ = Register(Service.UpdatePackage) | |||||
| type UpdatePackage struct { | type UpdatePackage struct { | ||||
| mq.MessageBodyBase | mq.MessageBodyBase | ||||
| PackageID cdssdk.PackageID `json:"packageID"` | PackageID cdssdk.PackageID `json:"packageID"` | ||||
| Adds []AddObjectInfo `json:"objects"` | |||||
| Adds []AddObjectEntry `json:"adds"` | |||||
| Deletes []cdssdk.ObjectID `json:"deletes"` | Deletes []cdssdk.ObjectID `json:"deletes"` | ||||
| } | } | ||||
| type UpdatePackageResp struct { | type UpdatePackageResp struct { | ||||
| mq.MessageBodyBase | mq.MessageBodyBase | ||||
| } | } | ||||
| type AddObjectInfo struct { | |||||
| type AddObjectEntry struct { | |||||
| Path string `json:"path"` | Path string `json:"path"` | ||||
| Size int64 `json:"size,string"` | Size int64 `json:"size,string"` | ||||
| FileHash string `json:"fileHash"` | FileHash string `json:"fileHash"` | ||||
| NodeID cdssdk.NodeID `json:"nodeID"` | NodeID cdssdk.NodeID `json:"nodeID"` | ||||
| } | } | ||||
| func NewUpdatePackage(packageID cdssdk.PackageID, adds []AddObjectInfo, deletes []cdssdk.ObjectID) *UpdatePackage { | |||||
| func NewUpdatePackage(packageID cdssdk.PackageID, adds []AddObjectEntry, deletes []cdssdk.ObjectID) *UpdatePackage { | |||||
| return &UpdatePackage{ | return &UpdatePackage{ | ||||
| PackageID: packageID, | PackageID: packageID, | ||||
| Adds: adds, | Adds: adds, | ||||
| @@ -108,8 +108,8 @@ func NewUpdatePackage(packageID cdssdk.PackageID, adds []AddObjectInfo, deletes | |||||
| func NewUpdatePackageResp() *UpdatePackageResp { | func NewUpdatePackageResp() *UpdatePackageResp { | ||||
| return &UpdatePackageResp{} | return &UpdatePackageResp{} | ||||
| } | } | ||||
| func NewAddObjectInfo(path string, size int64, fileHash string, nodeIDs cdssdk.NodeID) AddObjectInfo { | |||||
| return AddObjectInfo{ | |||||
| func NewAddObjectEntry(path string, size int64, fileHash string, nodeIDs cdssdk.NodeID) AddObjectEntry { | |||||
| return AddObjectEntry{ | |||||
| Path: path, | Path: path, | ||||
| Size: size, | Size: size, | ||||
| FileHash: fileHash, | FileHash: fileHash, | ||||
| @@ -117,7 +117,7 @@ func NewAddObjectInfo(path string, size int64, fileHash string, nodeIDs cdssdk.N | |||||
| } | } | ||||
| } | } | ||||
| func (client *Client) UpdateECPackage(msg *UpdatePackage) (*UpdatePackageResp, error) { | func (client *Client) UpdateECPackage(msg *UpdatePackage) (*UpdatePackageResp, error) { | ||||
| return mq.Request(Service.UpdateECPackage, client.rabbitCli, msg) | |||||
| return mq.Request(Service.UpdatePackage, client.rabbitCli, msg) | |||||
| } | } | ||||
| // 删除对象 | // 删除对象 | ||||
| @@ -1,6 +1,8 @@ | |||||
| package coordinator | package coordinator | ||||
| import ( | import ( | ||||
| "time" | |||||
| "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" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/db/model" | "gitlink.org.cn/cloudream/storage/common/pkgs/db/model" | ||||
| @@ -10,6 +12,8 @@ type StorageService interface { | |||||
| GetStorageInfo(msg *GetStorageInfo) (*GetStorageInfoResp, *mq.CodeMessage) | GetStorageInfo(msg *GetStorageInfo) (*GetStorageInfoResp, *mq.CodeMessage) | ||||
| StoragePackageLoaded(msg *StoragePackageLoaded) (*StoragePackageLoadedResp, *mq.CodeMessage) | StoragePackageLoaded(msg *StoragePackageLoaded) (*StoragePackageLoadedResp, *mq.CodeMessage) | ||||
| GetPackageLoadLogDetails(msg *GetPackageLoadLogDetails) (*GetPackageLoadLogDetailsResp, *mq.CodeMessage) | |||||
| } | } | ||||
| // 获取Storage信息 | // 获取Storage信息 | ||||
| @@ -72,3 +76,34 @@ func NewStoragePackageLoadedResp() *StoragePackageLoadedResp { | |||||
| func (client *Client) StoragePackageLoaded(msg *StoragePackageLoaded) (*StoragePackageLoadedResp, error) { | func (client *Client) StoragePackageLoaded(msg *StoragePackageLoaded) (*StoragePackageLoadedResp, error) { | ||||
| return mq.Request(Service.StoragePackageLoaded, client.rabbitCli, msg) | return mq.Request(Service.StoragePackageLoaded, client.rabbitCli, msg) | ||||
| } | } | ||||
| // 查询Package的导入记录 | |||||
| var _ = Register(Service.GetPackageLoadLogDetails) | |||||
| type GetPackageLoadLogDetails struct { | |||||
| mq.MessageBodyBase | |||||
| PackageID cdssdk.PackageID `json:"packageID"` | |||||
| } | |||||
| type GetPackageLoadLogDetailsResp struct { | |||||
| mq.MessageBodyBase | |||||
| Logs []PackageLoadLogDetail `json:"logs"` | |||||
| } | |||||
| type PackageLoadLogDetail struct { | |||||
| Storage model.Storage `json:"storage"` | |||||
| UserID cdssdk.UserID `json:"userID"` | |||||
| CreateTime time.Time `json:"createTime"` | |||||
| } | |||||
| func ReqGetPackageLoadLogDetails(packageID cdssdk.PackageID) *GetPackageLoadLogDetails { | |||||
| return &GetPackageLoadLogDetails{ | |||||
| PackageID: packageID, | |||||
| } | |||||
| } | |||||
| func RespGetPackageLoadLogDetails(logs []PackageLoadLogDetail) *GetPackageLoadLogDetailsResp { | |||||
| return &GetPackageLoadLogDetailsResp{ | |||||
| Logs: logs, | |||||
| } | |||||
| } | |||||
| func (client *Client) GetPackageLoadLogDetails(msg *GetPackageLoadLogDetails) (*GetPackageLoadLogDetailsResp, error) { | |||||
| return mq.Request(Service.GetPackageLoadLogDetails, client.rabbitCli, msg) | |||||
| } | |||||
| @@ -0,0 +1,18 @@ | |||||
| package event | |||||
| import cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | |||||
| type CheckPackageRedundancy struct { | |||||
| EventBase | |||||
| PackageID cdssdk.PackageID `json:"packageIDs"` | |||||
| } | |||||
| func NewCheckPackageRedundancy(packageID cdssdk.PackageID) *CheckPackageRedundancy { | |||||
| return &CheckPackageRedundancy{ | |||||
| PackageID: packageID, | |||||
| } | |||||
| } | |||||
| func init() { | |||||
| Register[*CheckPackageRedundancy]() | |||||
| } | |||||
| @@ -21,15 +21,26 @@ func (svc *Service) GetUserNodes(msg *coormq.GetUserNodes) (*coormq.GetUserNodes | |||||
| func (svc *Service) GetNodes(msg *coormq.GetNodes) (*coormq.GetNodesResp, *mq.CodeMessage) { | func (svc *Service) GetNodes(msg *coormq.GetNodes) (*coormq.GetNodesResp, *mq.CodeMessage) { | ||||
| var nodes []model.Node | var nodes []model.Node | ||||
| for _, id := range msg.NodeIDs { | |||||
| node, err := svc.db.Node().GetByID(svc.db.SQLCtx(), id) | |||||
| if msg.NodeIDs == nil { | |||||
| var err error | |||||
| nodes, err = svc.db.Node().GetAllNodes(svc.db.SQLCtx()) | |||||
| if err != nil { | if err != nil { | ||||
| logger.WithField("NodeID", id). | |||||
| Warnf("query node failed, err: %s", err.Error()) | |||||
| return nil, mq.Failed(errorcode.OperationFailed, "query node failed") | |||||
| logger.Warnf("getting all nodes: %s", err.Error()) | |||||
| return nil, mq.Failed(errorcode.OperationFailed, "get all node failed") | |||||
| } | } | ||||
| nodes = append(nodes, node) | |||||
| } else { | |||||
| for _, id := range msg.NodeIDs { | |||||
| node, err := svc.db.Node().GetByID(svc.db.SQLCtx(), id) | |||||
| if err != nil { | |||||
| logger.WithField("NodeID", id). | |||||
| Warnf("query node failed, err: %s", err.Error()) | |||||
| return nil, mq.Failed(errorcode.OperationFailed, "query node failed") | |||||
| } | |||||
| nodes = append(nodes, node) | |||||
| } | |||||
| } | } | ||||
| return mq.ReplyOK(coormq.NewGetNodesResp(nodes)) | return mq.ReplyOK(coormq.NewGetNodesResp(nodes)) | ||||
| @@ -31,3 +31,14 @@ func (svc *Service) GetPackageObjectDetails(msg *coormq.GetPackageObjectDetails) | |||||
| return mq.ReplyOK(coormq.NewGetPackageObjectDetailsResp(data)) | return mq.ReplyOK(coormq.NewGetPackageObjectDetailsResp(data)) | ||||
| } | } | ||||
| func (svc *Service) ChangeObjectRedundancy(msg *coormq.ChangeObjectRedundancy) (*coormq.ChangeObjectRedundancyResp, *mq.CodeMessage) { | |||||
| err := svc.db.Object().BatchUpdateRedundancy(svc.db.SQLCtx(), msg.Entries) | |||||
| if err != nil { | |||||
| logger.Warnf("batch updating redundancy: %s", err.Error()) | |||||
| return nil, mq.Failed(errorcode.OperationFailed, "batch update redundancy failed") | |||||
| } | |||||
| return mq.ReplyOK(coormq.RespChangeObjectRedundancy()) | |||||
| } | |||||
| @@ -10,6 +10,7 @@ import ( | |||||
| "gitlink.org.cn/cloudream/common/pkgs/logger" | "gitlink.org.cn/cloudream/common/pkgs/logger" | ||||
| "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" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/db/model" | |||||
| 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" | ||||
| scevt "gitlink.org.cn/cloudream/storage/common/pkgs/mq/scanner/event" | scevt "gitlink.org.cn/cloudream/storage/common/pkgs/mq/scanner/event" | ||||
| @@ -45,7 +46,7 @@ func (svc *Service) CreatePackage(msg *coormq.CreatePackage) (*coormq.CreatePack | |||||
| return mq.ReplyOK(coormq.NewCreatePackageResp(pkgID)) | return mq.ReplyOK(coormq.NewCreatePackageResp(pkgID)) | ||||
| } | } | ||||
| func (svc *Service) UpdateECPackage(msg *coormq.UpdatePackage) (*coormq.UpdatePackageResp, *mq.CodeMessage) { | |||||
| func (svc *Service) UpdatePackage(msg *coormq.UpdatePackage) (*coormq.UpdatePackageResp, *mq.CodeMessage) { | |||||
| _, err := svc.db.Package().GetByID(svc.db.SQLCtx(), msg.PackageID) | _, err := svc.db.Package().GetByID(svc.db.SQLCtx(), msg.PackageID) | ||||
| if err != nil { | if err != nil { | ||||
| logger.WithField("PackageID", msg.PackageID). | logger.WithField("PackageID", msg.PackageID). | ||||
| @@ -207,3 +208,37 @@ func (svc *Service) GetPackageLoadedNodes(msg *coormq.GetPackageLoadedNodes) (*c | |||||
| return mq.ReplyOK(coormq.NewGetPackageLoadedNodesResp(nodeIDs)) | return mq.ReplyOK(coormq.NewGetPackageLoadedNodesResp(nodeIDs)) | ||||
| } | } | ||||
| func (svc *Service) GetPackageLoadLogDetails(msg *coormq.GetPackageLoadLogDetails) (*coormq.GetPackageLoadLogDetailsResp, *mq.CodeMessage) { | |||||
| var logs []coormq.PackageLoadLogDetail | |||||
| rawLogs, err := svc.db.StoragePackageLog().GetByPackageID(svc.db.SQLCtx(), msg.PackageID) | |||||
| if err != nil { | |||||
| logger.WithField("PackageID", msg.PackageID). | |||||
| Warnf("getting storage package log: %s", err.Error()) | |||||
| return nil, mq.Failed(errorcode.OperationFailed, "get storage package log failed") | |||||
| } | |||||
| stgs := make(map[cdssdk.StorageID]model.Storage) | |||||
| for _, raw := range rawLogs { | |||||
| stg, ok := stgs[raw.StorageID] | |||||
| if !ok { | |||||
| stg, err = svc.db.Storage().GetByID(svc.db.SQLCtx(), raw.StorageID) | |||||
| if err != nil { | |||||
| logger.WithField("PackageID", msg.PackageID). | |||||
| Warnf("getting storage: %s", err.Error()) | |||||
| return nil, mq.Failed(errorcode.OperationFailed, "get storage failed") | |||||
| } | |||||
| stgs[raw.StorageID] = stg | |||||
| } | |||||
| logs = append(logs, coormq.PackageLoadLogDetail{ | |||||
| Storage: stg, | |||||
| UserID: raw.UserID, | |||||
| CreateTime: raw.CreateTime, | |||||
| }) | |||||
| } | |||||
| return mq.ReplyOK(coormq.RespGetPackageLoadLogDetails(logs)) | |||||
| } | |||||
| @@ -9,8 +9,8 @@ import ( | |||||
| ) | ) | ||||
| type Config struct { | type Config struct { | ||||
| MinAvailableRepProportion float32 `json:"minAvailableRepProportion"` // 可用的备份至少要占所有备份的比例,向上去整 | |||||
| NodeUnavailableSeconds int `json:"nodeUnavailableSeconds"` // 如果节点上次上报时间超过这个值,则认为节点已经不可用 | |||||
| ECFileSizeThreshold int64 `json:"ecFileSizeThreshold"` | |||||
| NodeUnavailableSeconds int `json:"nodeUnavailableSeconds"` // 如果节点上次上报时间超过这个值,则认为节点已经不可用 | |||||
| Logger log.Config `json:"logger"` | Logger log.Config `json:"logger"` | ||||
| DB db.Config `json:"db"` | DB db.Config `json:"db"` | ||||
| @@ -0,0 +1,629 @@ | |||||
| package event | |||||
| import ( | |||||
| "fmt" | |||||
| "time" | |||||
| "github.com/samber/lo" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/logger" | |||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | |||||
| "gitlink.org.cn/cloudream/common/utils/sort" | |||||
| stgglb "gitlink.org.cn/cloudream/storage/common/globals" | |||||
| stgmod "gitlink.org.cn/cloudream/storage/common/models" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/db/model" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch/plans" | |||||
| agtmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/agent" | |||||
| coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator" | |||||
| scevt "gitlink.org.cn/cloudream/storage/common/pkgs/mq/scanner/event" | |||||
| "gitlink.org.cn/cloudream/storage/scanner/internal/config" | |||||
| ) | |||||
| const ( | |||||
| monthHours = 30 * 24 | |||||
| yearHours = 365 * 24 | |||||
| ) | |||||
| type CheckPackageRedundancy struct { | |||||
| *scevt.CheckPackageRedundancy | |||||
| } | |||||
| func NewCheckPackageRedundancy(evt *scevt.CheckPackageRedundancy) *CheckPackageRedundancy { | |||||
| return &CheckPackageRedundancy{ | |||||
| CheckPackageRedundancy: evt, | |||||
| } | |||||
| } | |||||
| type NodeLoadInfo struct { | |||||
| Node model.Node | |||||
| LoadsRecentMonth int | |||||
| LoadsRecentYear int | |||||
| } | |||||
| func (t *CheckPackageRedundancy) TryMerge(other Event) bool { | |||||
| event, ok := other.(*CheckPackageRedundancy) | |||||
| if !ok { | |||||
| return false | |||||
| } | |||||
| return event.PackageID == t.PackageID | |||||
| } | |||||
| func (t *CheckPackageRedundancy) Execute(execCtx ExecuteContext) { | |||||
| log := logger.WithType[CheckPackageRedundancy]("Event") | |||||
| log.Debugf("begin with %v", logger.FormatStruct(t.CheckPackageRedundancy)) | |||||
| defer log.Debugf("end") | |||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | |||||
| if err != nil { | |||||
| log.Warnf("new coordinator client: %s", err.Error()) | |||||
| return | |||||
| } | |||||
| defer stgglb.CoordinatorMQPool.Release(coorCli) | |||||
| getObjs, err := coorCli.GetPackageObjectDetails(coormq.NewGetPackageObjectDetails(t.PackageID)) | |||||
| if err != nil { | |||||
| log.Warnf("getting package objects: %s", err.Error()) | |||||
| return | |||||
| } | |||||
| getLogs, err := coorCli.GetPackageLoadLogDetails(coormq.ReqGetPackageLoadLogDetails(t.PackageID)) | |||||
| if err != nil { | |||||
| log.Warnf("getting package load log details: %s", err.Error()) | |||||
| return | |||||
| } | |||||
| getNodes, err := coorCli.GetNodes(coormq.NewGetNodes(nil)) | |||||
| if err != nil { | |||||
| log.Warnf("getting all nodes: %s", err.Error()) | |||||
| return | |||||
| } | |||||
| if len(getNodes.Nodes) == 0 { | |||||
| log.Warnf("no available nodes") | |||||
| return | |||||
| } | |||||
| allNodes := make(map[cdssdk.NodeID]*NodeLoadInfo) | |||||
| for _, node := range getNodes.Nodes { | |||||
| allNodes[node.NodeID] = &NodeLoadInfo{ | |||||
| Node: node, | |||||
| } | |||||
| } | |||||
| for _, log := range getLogs.Logs { | |||||
| info, ok := allNodes[log.Storage.NodeID] | |||||
| if !ok { | |||||
| continue | |||||
| } | |||||
| sinceNow := time.Since(log.CreateTime) | |||||
| if sinceNow.Hours() < monthHours { | |||||
| info.LoadsRecentMonth++ | |||||
| } else if sinceNow.Hours() < yearHours { | |||||
| info.LoadsRecentYear++ | |||||
| } | |||||
| } | |||||
| var changedObjects []coormq.ChangeObjectRedundancyEntry | |||||
| defRep := cdssdk.DefaultRepRedundancy | |||||
| defEC := cdssdk.DefaultECRedundancy | |||||
| newRepNodes := t.chooseNewNodesForRep(&defRep, allNodes) | |||||
| newECNodes := t.chooseNewNodesForEC(&defEC, allNodes) | |||||
| for _, obj := range getObjs.Objects { | |||||
| var entry *coormq.ChangeObjectRedundancyEntry | |||||
| var err error | |||||
| shouldUseEC := obj.Object.Size > config.Cfg().ECFileSizeThreshold | |||||
| switch red := obj.Object.Redundancy.(type) { | |||||
| case *cdssdk.NoneRedundancy: | |||||
| if shouldUseEC { | |||||
| log.WithField("ObjectID", obj.Object.ObjectID).Debugf("redundancy: none -> ec") | |||||
| entry, err = t.noneToEC(obj, &defEC, newECNodes) | |||||
| } else { | |||||
| log.WithField("ObjectID", obj.Object.ObjectID).Debugf("redundancy: none -> rep") | |||||
| entry, err = t.noneToRep(obj, &defRep, newRepNodes) | |||||
| } | |||||
| case *cdssdk.RepRedundancy: | |||||
| if shouldUseEC { | |||||
| log.WithField("ObjectID", obj.Object.ObjectID).Debugf("redundancy: rep -> ec") | |||||
| entry, err = t.repToEC(obj, &defEC, newECNodes) | |||||
| } else { | |||||
| uploadNodes := t.rechooseNodesForRep(obj, red, allNodes) | |||||
| entry, err = t.repToRep(obj, &defRep, uploadNodes) | |||||
| } | |||||
| case *cdssdk.ECRedundancy: | |||||
| if shouldUseEC { | |||||
| uploadNodes := t.rechooseNodesForEC(obj, red, allNodes) | |||||
| entry, err = t.ecToEC(obj, red, &defEC, uploadNodes) | |||||
| } else { | |||||
| log.WithField("ObjectID", obj.Object.ObjectID).Debugf("redundancy: ec -> rep") | |||||
| entry, err = t.ecToRep(obj, red, &defRep, newRepNodes) | |||||
| } | |||||
| } | |||||
| if entry != nil { | |||||
| changedObjects = append(changedObjects, *entry) | |||||
| } | |||||
| if err != nil { | |||||
| log.WithField("ObjectID", obj.Object.ObjectID).Warnf("%s, its redundancy wont be changed", err.Error()) | |||||
| } | |||||
| } | |||||
| if len(changedObjects) == 0 { | |||||
| return | |||||
| } | |||||
| _, err = coorCli.ChangeObjectRedundancy(coormq.ReqChangeObjectRedundancy(changedObjects)) | |||||
| if err != nil { | |||||
| log.Warnf("requesting to change object redundancy: %s", err.Error()) | |||||
| return | |||||
| } | |||||
| } | |||||
| func (t *CheckPackageRedundancy) chooseNewNodesForRep(red *cdssdk.RepRedundancy, allNodes map[cdssdk.NodeID]*NodeLoadInfo) []*NodeLoadInfo { | |||||
| sortedNodes := sort.Sort(lo.Values(allNodes), func(left *NodeLoadInfo, right *NodeLoadInfo) int { | |||||
| dm := right.LoadsRecentMonth - left.LoadsRecentMonth | |||||
| if dm != 0 { | |||||
| return dm | |||||
| } | |||||
| return right.LoadsRecentYear - left.LoadsRecentYear | |||||
| }) | |||||
| return t.chooseSoManyNodes(red.RepCount, sortedNodes) | |||||
| } | |||||
| func (t *CheckPackageRedundancy) chooseNewNodesForEC(red *cdssdk.ECRedundancy, allNodes map[cdssdk.NodeID]*NodeLoadInfo) []*NodeLoadInfo { | |||||
| sortedNodes := sort.Sort(lo.Values(allNodes), func(left *NodeLoadInfo, right *NodeLoadInfo) int { | |||||
| dm := right.LoadsRecentMonth - left.LoadsRecentMonth | |||||
| if dm != 0 { | |||||
| return dm | |||||
| } | |||||
| return right.LoadsRecentYear - left.LoadsRecentYear | |||||
| }) | |||||
| return t.chooseSoManyNodes(red.N, sortedNodes) | |||||
| } | |||||
| func (t *CheckPackageRedundancy) rechooseNodesForRep(obj stgmod.ObjectDetail, red *cdssdk.RepRedundancy, allNodes map[cdssdk.NodeID]*NodeLoadInfo) []*NodeLoadInfo { | |||||
| type rechooseNode struct { | |||||
| *NodeLoadInfo | |||||
| CachedBlockIndex int | |||||
| } | |||||
| var rechooseNodes []*rechooseNode | |||||
| for _, node := range allNodes { | |||||
| cachedBlockIndex := -1 | |||||
| for _, block := range obj.Blocks { | |||||
| if lo.Contains(block.CachedNodeIDs, node.Node.NodeID) { | |||||
| cachedBlockIndex = block.Index | |||||
| break | |||||
| } | |||||
| } | |||||
| rechooseNodes = append(rechooseNodes, &rechooseNode{ | |||||
| NodeLoadInfo: node, | |||||
| CachedBlockIndex: cachedBlockIndex, | |||||
| }) | |||||
| } | |||||
| sortedNodes := sort.Sort(rechooseNodes, func(left *rechooseNode, right *rechooseNode) int { | |||||
| dm := right.LoadsRecentMonth - left.LoadsRecentMonth | |||||
| if dm != 0 { | |||||
| return dm | |||||
| } | |||||
| // 已经缓存了文件块的节点优先选择 | |||||
| v := sort.CmpBool(right.CachedBlockIndex > -1, left.CachedBlockIndex > -1) | |||||
| if v != 0 { | |||||
| return v | |||||
| } | |||||
| return right.LoadsRecentYear - left.LoadsRecentYear | |||||
| }) | |||||
| return t.chooseSoManyNodes(red.RepCount, lo.Map(sortedNodes, func(node *rechooseNode, idx int) *NodeLoadInfo { return node.NodeLoadInfo })) | |||||
| } | |||||
| func (t *CheckPackageRedundancy) rechooseNodesForEC(obj stgmod.ObjectDetail, red *cdssdk.ECRedundancy, allNodes map[cdssdk.NodeID]*NodeLoadInfo) []*NodeLoadInfo { | |||||
| type rechooseNode struct { | |||||
| *NodeLoadInfo | |||||
| CachedBlockIndex int | |||||
| } | |||||
| var rechooseNodes []*rechooseNode | |||||
| for _, node := range allNodes { | |||||
| cachedBlockIndex := -1 | |||||
| for _, block := range obj.Blocks { | |||||
| if lo.Contains(block.CachedNodeIDs, node.Node.NodeID) { | |||||
| cachedBlockIndex = block.Index | |||||
| break | |||||
| } | |||||
| } | |||||
| rechooseNodes = append(rechooseNodes, &rechooseNode{ | |||||
| NodeLoadInfo: node, | |||||
| CachedBlockIndex: cachedBlockIndex, | |||||
| }) | |||||
| } | |||||
| sortedNodes := sort.Sort(rechooseNodes, func(left *rechooseNode, right *rechooseNode) int { | |||||
| dm := right.LoadsRecentMonth - left.LoadsRecentMonth | |||||
| if dm != 0 { | |||||
| return dm | |||||
| } | |||||
| // 已经缓存了文件块的节点优先选择 | |||||
| v := sort.CmpBool(right.CachedBlockIndex > -1, left.CachedBlockIndex > -1) | |||||
| if v != 0 { | |||||
| return v | |||||
| } | |||||
| return right.LoadsRecentYear - left.LoadsRecentYear | |||||
| }) | |||||
| // TODO 可以考虑选择已有块的节点时,能依然按照Index顺序选择 | |||||
| return t.chooseSoManyNodes(red.N, lo.Map(sortedNodes, func(node *rechooseNode, idx int) *NodeLoadInfo { return node.NodeLoadInfo })) | |||||
| } | |||||
| func (t *CheckPackageRedundancy) chooseSoManyNodes(count int, nodes []*NodeLoadInfo) []*NodeLoadInfo { | |||||
| repeateCount := (count + len(nodes) - 1) / len(nodes) | |||||
| extedNodes := make([]*NodeLoadInfo, repeateCount*len(nodes)) | |||||
| // 使用复制的方式将节点数扩充到要求的数量 | |||||
| // 复制之后的结构:ABCD -> AAABBBCCCDDD | |||||
| for p := 0; p < repeateCount; p++ { | |||||
| for i, node := range nodes { | |||||
| putIdx := i*repeateCount + p | |||||
| extedNodes[putIdx] = node | |||||
| } | |||||
| } | |||||
| extedNodes = extedNodes[:count] | |||||
| var chosen []*NodeLoadInfo | |||||
| for len(chosen) < count { | |||||
| // 在每一轮内都选不同地区的节点,如果节点数不够,那么就再来一轮 | |||||
| chosenLocations := make(map[cdssdk.LocationID]bool) | |||||
| for i, node := range extedNodes { | |||||
| if node == nil { | |||||
| continue | |||||
| } | |||||
| if chosenLocations[node.Node.LocationID] { | |||||
| continue | |||||
| } | |||||
| chosen = append(chosen, node) | |||||
| chosenLocations[node.Node.LocationID] = true | |||||
| extedNodes[i] = nil | |||||
| } | |||||
| } | |||||
| return chosen | |||||
| } | |||||
| func (t *CheckPackageRedundancy) noneToRep(obj stgmod.ObjectDetail, red *cdssdk.RepRedundancy, uploadNodes []*NodeLoadInfo) (*coormq.ChangeObjectRedundancyEntry, error) { | |||||
| if len(obj.CachedNodeIDs) == 0 { | |||||
| return nil, fmt.Errorf("object is not cached on any nodes, cannot change its redundancy to rep") | |||||
| } | |||||
| // 如果选择的备份节点都是同一个,那么就只要上传一次 | |||||
| uploadNodes = lo.UniqBy(uploadNodes, func(item *NodeLoadInfo) cdssdk.NodeID { return item.Node.NodeID }) | |||||
| var blocks []stgmod.ObjectBlock | |||||
| for _, node := range uploadNodes { | |||||
| err := t.pinObject(node.Node.NodeID, obj.Object.FileHash) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| blocks = append(blocks, stgmod.ObjectBlock{ | |||||
| ObjectID: obj.Object.ObjectID, | |||||
| Index: 0, | |||||
| NodeID: node.Node.NodeID, | |||||
| FileHash: obj.Object.FileHash, | |||||
| }) | |||||
| } | |||||
| return &coormq.ChangeObjectRedundancyEntry{ | |||||
| ObjectID: obj.Object.ObjectID, | |||||
| Redundancy: red, | |||||
| Blocks: blocks, | |||||
| }, nil | |||||
| } | |||||
| func (t *CheckPackageRedundancy) noneToEC(obj stgmod.ObjectDetail, red *cdssdk.ECRedundancy, uploadNodes []*NodeLoadInfo) (*coormq.ChangeObjectRedundancyEntry, error) { | |||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("new coordinator client: %w", err) | |||||
| } | |||||
| defer stgglb.CoordinatorMQPool.Release(coorCli) | |||||
| if len(obj.CachedNodeIDs) == 0 { | |||||
| return nil, fmt.Errorf("object is not cached on any nodes, cannot change its redundancy to ec") | |||||
| } | |||||
| getNodes, err := coorCli.GetNodes(coormq.NewGetNodes([]cdssdk.NodeID{obj.CachedNodeIDs[0]})) | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("requesting to get nodes: %w", err) | |||||
| } | |||||
| planBlder := plans.NewPlanBuilder() | |||||
| inputStrs := planBlder.AtAgent(getNodes.Nodes[0]).IPFSRead(obj.Object.FileHash).ChunkedSplit(red.ChunkSize, red.K, true) | |||||
| outputStrs := planBlder.AtAgent(getNodes.Nodes[0]).ECReconstructAny(*red, lo.Range(red.K), lo.Range(red.N), inputStrs.Streams...) | |||||
| for i := 0; i < red.N; i++ { | |||||
| outputStrs.Stream(i).GRPCSend(uploadNodes[i].Node).IPFSWrite(fmt.Sprintf("%d", i)) | |||||
| } | |||||
| plan, err := planBlder.Build() | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("building io plan: %w", err) | |||||
| } | |||||
| exec, err := plans.Execute(*plan) | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("executing io plan: %w", err) | |||||
| } | |||||
| ioRet, err := exec.Wait() | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("executing io plan: %w", err) | |||||
| } | |||||
| var blocks []stgmod.ObjectBlock | |||||
| for i := 0; i < red.N; i++ { | |||||
| blocks = append(blocks, stgmod.ObjectBlock{ | |||||
| ObjectID: obj.Object.ObjectID, | |||||
| Index: i, | |||||
| NodeID: uploadNodes[i].Node.NodeID, | |||||
| FileHash: ioRet.ResultValues[fmt.Sprintf("%d", i)].(string), | |||||
| }) | |||||
| } | |||||
| return &coormq.ChangeObjectRedundancyEntry{ | |||||
| ObjectID: obj.Object.ObjectID, | |||||
| Redundancy: red, | |||||
| Blocks: blocks, | |||||
| }, nil | |||||
| } | |||||
| func (t *CheckPackageRedundancy) repToRep(obj stgmod.ObjectDetail, red *cdssdk.RepRedundancy, uploadNodes []*NodeLoadInfo) (*coormq.ChangeObjectRedundancyEntry, error) { | |||||
| if len(obj.CachedNodeIDs) == 0 { | |||||
| return nil, fmt.Errorf("object is not cached on any nodes, cannot change its redundancy to rep") | |||||
| } | |||||
| // 如果选择的备份节点都是同一个,那么就只要上传一次 | |||||
| uploadNodes = lo.UniqBy(uploadNodes, func(item *NodeLoadInfo) cdssdk.NodeID { return item.Node.NodeID }) | |||||
| for _, node := range uploadNodes { | |||||
| err := t.pinObject(node.Node.NodeID, obj.Object.FileHash) | |||||
| if err != nil { | |||||
| logger.WithField("ObjectID", obj.Object.ObjectID). | |||||
| Warn(err.Error()) | |||||
| return nil, err | |||||
| } | |||||
| } | |||||
| var blocks []stgmod.ObjectBlock | |||||
| for _, node := range uploadNodes { | |||||
| // 由于更新冗余方式会删除所有Block记录然后重新填充, | |||||
| // 所以即使是节点跳过了上传,也需要为它添加一条Block记录 | |||||
| blocks = append(blocks, stgmod.ObjectBlock{ | |||||
| ObjectID: obj.Object.ObjectID, | |||||
| Index: 0, | |||||
| NodeID: node.Node.NodeID, | |||||
| FileHash: obj.Object.FileHash, | |||||
| }) | |||||
| } | |||||
| return &coormq.ChangeObjectRedundancyEntry{ | |||||
| ObjectID: obj.Object.ObjectID, | |||||
| Redundancy: red, | |||||
| Blocks: blocks, | |||||
| }, nil | |||||
| } | |||||
| func (t *CheckPackageRedundancy) repToEC(obj stgmod.ObjectDetail, red *cdssdk.ECRedundancy, uploadNodes []*NodeLoadInfo) (*coormq.ChangeObjectRedundancyEntry, error) { | |||||
| return t.noneToEC(obj, red, uploadNodes) | |||||
| } | |||||
| func (t *CheckPackageRedundancy) ecToRep(obj stgmod.ObjectDetail, srcRed *cdssdk.ECRedundancy, tarRed *cdssdk.RepRedundancy, uploadNodes []*NodeLoadInfo) (*coormq.ChangeObjectRedundancyEntry, error) { | |||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("new coordinator client: %w", err) | |||||
| } | |||||
| defer stgglb.CoordinatorMQPool.Release(coorCli) | |||||
| var chosenBlocks []stgmod.ObjectBlockDetail | |||||
| var chosenBlockIndexes []int | |||||
| for _, block := range obj.Blocks { | |||||
| if len(block.CachedNodeIDs) > 0 { | |||||
| chosenBlocks = append(chosenBlocks, block) | |||||
| chosenBlockIndexes = append(chosenBlockIndexes, block.Index) | |||||
| } | |||||
| if len(chosenBlocks) == srcRed.K { | |||||
| break | |||||
| } | |||||
| } | |||||
| if len(chosenBlocks) < srcRed.K { | |||||
| return nil, fmt.Errorf("no enough blocks to reconstruct the original file data") | |||||
| } | |||||
| // 如果选择的备份节点都是同一个,那么就只要上传一次 | |||||
| uploadNodes = lo.UniqBy(uploadNodes, func(item *NodeLoadInfo) cdssdk.NodeID { return item.Node.NodeID }) | |||||
| // 每个被选节点都在自己节点上重建原始数据 | |||||
| planBlder := plans.NewPlanBuilder() | |||||
| for i := range uploadNodes { | |||||
| tarNode := planBlder.AtAgent(uploadNodes[i].Node) | |||||
| var inputs []*plans.AgentStream | |||||
| for _, block := range chosenBlocks { | |||||
| inputs = append(inputs, tarNode.IPFSRead(block.FileHash)) | |||||
| } | |||||
| outputs := tarNode.ECReconstruct(*srcRed, chosenBlockIndexes, inputs...) | |||||
| tarNode.ChunkedJoin(srcRed.ChunkSize, outputs.Streams...).Length(obj.Object.Size).IPFSWrite(fmt.Sprintf("%d", i)) | |||||
| } | |||||
| plan, err := planBlder.Build() | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("building io plan: %w", err) | |||||
| } | |||||
| exec, err := plans.Execute(*plan) | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("executing io plan: %w", err) | |||||
| } | |||||
| ioRet, err := exec.Wait() | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("executing io plan: %w", err) | |||||
| } | |||||
| var blocks []stgmod.ObjectBlock | |||||
| for i := range uploadNodes { | |||||
| blocks = append(blocks, stgmod.ObjectBlock{ | |||||
| ObjectID: obj.Object.ObjectID, | |||||
| Index: 0, | |||||
| NodeID: uploadNodes[i].Node.NodeID, | |||||
| FileHash: ioRet.ResultValues[fmt.Sprintf("%d", i)].(string), | |||||
| }) | |||||
| } | |||||
| return &coormq.ChangeObjectRedundancyEntry{ | |||||
| ObjectID: obj.Object.ObjectID, | |||||
| Redundancy: tarRed, | |||||
| Blocks: blocks, | |||||
| }, nil | |||||
| } | |||||
| func (t *CheckPackageRedundancy) ecToEC(obj stgmod.ObjectDetail, srcRed *cdssdk.ECRedundancy, tarRed *cdssdk.ECRedundancy, uploadNodes []*NodeLoadInfo) (*coormq.ChangeObjectRedundancyEntry, error) { | |||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("new coordinator client: %w", err) | |||||
| } | |||||
| defer stgglb.CoordinatorMQPool.Release(coorCli) | |||||
| var chosenBlocks []stgmod.ObjectBlockDetail | |||||
| var chosenBlockIndexes []int | |||||
| for _, block := range obj.Blocks { | |||||
| if len(block.CachedNodeIDs) > 0 { | |||||
| chosenBlocks = append(chosenBlocks, block) | |||||
| chosenBlockIndexes = append(chosenBlockIndexes, block.Index) | |||||
| } | |||||
| if len(chosenBlocks) == srcRed.K { | |||||
| break | |||||
| } | |||||
| } | |||||
| if len(chosenBlocks) < srcRed.K { | |||||
| return nil, fmt.Errorf("no enough blocks to reconstruct the original file data") | |||||
| } | |||||
| // 目前EC的参数都相同,所以可以不用重建出完整数据然后再分块,可以直接构建出目的节点需要的块 | |||||
| planBlder := plans.NewPlanBuilder() | |||||
| var newBlocks []stgmod.ObjectBlock | |||||
| shouldUpdateBlocks := false | |||||
| for i := range obj.Blocks { | |||||
| newBlocks = append(newBlocks, stgmod.ObjectBlock{ | |||||
| ObjectID: obj.Object.ObjectID, | |||||
| Index: i, | |||||
| NodeID: uploadNodes[i].Node.NodeID, | |||||
| FileHash: obj.Blocks[i].FileHash, | |||||
| }) | |||||
| // 如果新选中的节点已经记录在Block表中,那么就不需要任何变更 | |||||
| if lo.Contains(obj.Blocks[i].NodeIDs, uploadNodes[i].Node.NodeID) { | |||||
| continue | |||||
| } | |||||
| shouldUpdateBlocks = true | |||||
| // 新选的节点不在Block表中,但实际上保存了分块的数据,那么只需建立一条Block记录即可 | |||||
| if lo.Contains(obj.Blocks[i].CachedNodeIDs, uploadNodes[i].Node.NodeID) { | |||||
| continue | |||||
| } | |||||
| // 否则就要重建出这个节点需要的块 | |||||
| tarNode := planBlder.AtAgent(uploadNodes[i].Node) | |||||
| var inputs []*plans.AgentStream | |||||
| for _, block := range chosenBlocks { | |||||
| inputs = append(inputs, tarNode.IPFSRead(block.FileHash)) | |||||
| } | |||||
| // 输出只需要自己要保存的那一块 | |||||
| tarNode.ECReconstructAny(*srcRed, chosenBlockIndexes, []int{i}, inputs...).Stream(0).IPFSWrite("") | |||||
| } | |||||
| plan, err := planBlder.Build() | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("building io plan: %w", err) | |||||
| } | |||||
| exec, err := plans.Execute(*plan) | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("executing io plan: %w", err) | |||||
| } | |||||
| // 如果没有任何Plan,Wait会直接返回成功 | |||||
| _, err = exec.Wait() | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("executing io plan: %w", err) | |||||
| } | |||||
| if !shouldUpdateBlocks { | |||||
| return nil, nil | |||||
| } | |||||
| return &coormq.ChangeObjectRedundancyEntry{ | |||||
| ObjectID: obj.Object.ObjectID, | |||||
| Redundancy: tarRed, | |||||
| Blocks: newBlocks, | |||||
| }, nil | |||||
| } | |||||
| func (t *CheckPackageRedundancy) pinObject(nodeID cdssdk.NodeID, fileHash string) error { | |||||
| agtCli, err := stgglb.AgentMQPool.Acquire(nodeID) | |||||
| if err != nil { | |||||
| return fmt.Errorf("new agent client: %w", err) | |||||
| } | |||||
| defer stgglb.AgentMQPool.Release(agtCli) | |||||
| pinObjResp, err := agtCli.StartPinningObject(agtmq.NewStartPinningObject(fileHash)) | |||||
| if err != nil { | |||||
| return fmt.Errorf("start pinning object: %w", err) | |||||
| } | |||||
| for { | |||||
| waitResp, err := agtCli.WaitPinningObject(agtmq.NewWaitPinningObject(pinObjResp.TaskID, int64(time.Second)*5)) | |||||
| if err != nil { | |||||
| return fmt.Errorf("waitting pinning object: %w", err) | |||||
| } | |||||
| if waitResp.IsComplete { | |||||
| if waitResp.Error != "" { | |||||
| return fmt.Errorf("agent pinning object: %s", waitResp.Error) | |||||
| } | |||||
| break | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func init() { | |||||
| RegisterMessageConvertor(NewCheckPackageRedundancy) | |||||
| } | |||||
| @@ -42,6 +42,8 @@ func FromMessage(msg scevt.Event) (Event, error) { | |||||
| return event, nil | return event, nil | ||||
| } | } | ||||
| func RegisterMessageConvertor[T any](converter func(msg T) Event) { | |||||
| typedispatcher.Add(msgDispatcher, converter) | |||||
| func RegisterMessageConvertor[T any, TEvt Event](converter func(msg T) TEvt) { | |||||
| typedispatcher.Add(msgDispatcher, func(msg T) Event { | |||||
| return converter(msg) | |||||
| }) | |||||
| } | } | ||||
| @@ -0,0 +1,76 @@ | |||||
| package event | |||||
| import ( | |||||
| "testing" | |||||
| "github.com/samber/lo" | |||||
| . "github.com/smartystreets/goconvey/convey" | |||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/db/model" | |||||
| ) | |||||
| func Test_chooseSoManyNodes(t *testing.T) { | |||||
| testcases := []struct { | |||||
| title string | |||||
| allNodes []*NodeLoadInfo | |||||
| count int | |||||
| expectedNodeIDs []cdssdk.NodeID | |||||
| }{ | |||||
| { | |||||
| title: "节点数量充足", | |||||
| allNodes: []*NodeLoadInfo{ | |||||
| {Node: model.Node{NodeID: cdssdk.NodeID(1)}}, | |||||
| {Node: model.Node{NodeID: cdssdk.NodeID(2)}}, | |||||
| }, | |||||
| count: 2, | |||||
| expectedNodeIDs: []cdssdk.NodeID{1, 2}, | |||||
| }, | |||||
| { | |||||
| title: "节点数量超过", | |||||
| allNodes: []*NodeLoadInfo{ | |||||
| {Node: model.Node{NodeID: cdssdk.NodeID(1)}}, | |||||
| {Node: model.Node{NodeID: cdssdk.NodeID(2)}}, | |||||
| {Node: model.Node{NodeID: cdssdk.NodeID(3)}}, | |||||
| }, | |||||
| count: 2, | |||||
| expectedNodeIDs: []cdssdk.NodeID{1, 2}, | |||||
| }, | |||||
| { | |||||
| title: "只有一个节点,节点数量不够", | |||||
| allNodes: []*NodeLoadInfo{ | |||||
| {Node: model.Node{NodeID: cdssdk.NodeID(1)}}, | |||||
| }, | |||||
| count: 3, | |||||
| expectedNodeIDs: []cdssdk.NodeID{1, 1, 1}, | |||||
| }, | |||||
| { | |||||
| title: "多个同地区节点,节点数量不够", | |||||
| allNodes: []*NodeLoadInfo{ | |||||
| {Node: model.Node{NodeID: cdssdk.NodeID(1)}}, | |||||
| {Node: model.Node{NodeID: cdssdk.NodeID(2)}}, | |||||
| }, | |||||
| count: 5, | |||||
| expectedNodeIDs: []cdssdk.NodeID{1, 1, 1, 2, 2}, | |||||
| }, | |||||
| { | |||||
| title: "节点数量不够,且在不同地区", | |||||
| allNodes: []*NodeLoadInfo{ | |||||
| {Node: model.Node{NodeID: cdssdk.NodeID(1), LocationID: cdssdk.LocationID(1)}}, | |||||
| {Node: model.Node{NodeID: cdssdk.NodeID(2), LocationID: cdssdk.LocationID(2)}}, | |||||
| }, | |||||
| count: 5, | |||||
| expectedNodeIDs: []cdssdk.NodeID{1, 2, 1, 2, 1}, | |||||
| }, | |||||
| } | |||||
| for _, test := range testcases { | |||||
| Convey(test.title, t, func() { | |||||
| var t CheckPackageRedundancy | |||||
| chosenNodes := t.chooseSoManyNodes(test.count, test.allNodes) | |||||
| chosenNodeIDs := lo.Map(chosenNodes, func(item *NodeLoadInfo, idx int) cdssdk.NodeID { return item.Node.NodeID }) | |||||
| So(chosenNodeIDs, ShouldResemble, test.expectedNodeIDs) | |||||
| }) | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,48 @@ | |||||
| package tickevent | |||||
| import ( | |||||
| "time" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/logger" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/mq/scanner/event" | |||||
| evt "gitlink.org.cn/cloudream/storage/scanner/internal/event" | |||||
| ) | |||||
| type BatchCheckPackageRedundancy struct { | |||||
| lastCheckStart int | |||||
| } | |||||
| func NewBatchCheckPackageRedundancy() *BatchCheckPackageRedundancy { | |||||
| return &BatchCheckPackageRedundancy{} | |||||
| } | |||||
| func (e *BatchCheckPackageRedundancy) Execute(ctx ExecuteContext) { | |||||
| log := logger.WithType[BatchCheckPackageRedundancy]("TickEvent") | |||||
| log.Debugf("begin") | |||||
| defer log.Debugf("end") | |||||
| // TODO 更好的策略 | |||||
| nowHour := time.Now().Hour() | |||||
| if nowHour > 6 { | |||||
| return | |||||
| } | |||||
| packageIDs, err := ctx.Args.DB.Package().BatchGetAllPackageIDs(ctx.Args.DB.SQLCtx(), e.lastCheckStart, CheckPackageBatchSize) | |||||
| if err != nil { | |||||
| log.Warnf("batch get package ids failed, err: %s", err.Error()) | |||||
| return | |||||
| } | |||||
| for _, id := range packageIDs { | |||||
| ctx.Args.EventExecutor.Post(evt.NewCheckPackageRedundancy(event.NewCheckPackageRedundancy(id))) | |||||
| } | |||||
| // 如果结果的长度小于预期的长度,则认为已经查询了所有,下次从头再来 | |||||
| if len(packageIDs) < CheckPackageBatchSize { | |||||
| e.lastCheckStart = 0 | |||||
| log.Debugf("all package checked, next time will start check at offset 0") | |||||
| } else { | |||||
| e.lastCheckStart += CheckPackageBatchSize | |||||
| } | |||||
| } | |||||
| @@ -126,4 +126,6 @@ func startTickEvent(tickExecutor *tickevent.Executor) { | |||||
| tickExecutor.Start(tickevent.NewCheckAgentState(), 5*60*1000, tickevent.StartOption{RandomStartDelayMs: 60 * 1000}) | tickExecutor.Start(tickevent.NewCheckAgentState(), 5*60*1000, tickevent.StartOption{RandomStartDelayMs: 60 * 1000}) | ||||
| tickExecutor.Start(tickevent.NewCheckCache(), interval, tickevent.StartOption{RandomStartDelayMs: 60 * 1000}) | tickExecutor.Start(tickevent.NewCheckCache(), interval, tickevent.StartOption{RandomStartDelayMs: 60 * 1000}) | ||||
| tickExecutor.Start(tickevent.NewBatchCheckPackageRedundancy(), interval, tickevent.StartOption{RandomStartDelayMs: 10 * 60 * 1000}) | |||||
| } | } | ||||