package ticktock import ( "fmt" "time" "gitlink.org.cn/cloudream/common/pkgs/logger" "gitlink.org.cn/cloudream/common/utils/reflect2" "gitlink.org.cn/cloudream/jcs-pub/client/internal/db" clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types" "gitlink.org.cn/cloudream/jcs-pub/common/models/datamap" "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/reqbuilder" ) const ( BatchGetPackageDetailCount = 100 BatchGetObjectDetailCount = 1000 ) type ChangeRedundancy struct { } func (j *ChangeRedundancy) Name() string { return reflect2.TypeNameOf[ChangeRedundancy]() } func (j *ChangeRedundancy) Execute(t *TickTock) { log := logger.WithType[ChangeRedundancy]("TickTock") startTime := time.Now() log.Debugf("job start") defer func() { log.Debugf("job end, time: %v", time.Since(startTime)) }() ctx := &changeRedundancyContext{ ticktock: t, allUserSpaces: make(map[clitypes.UserSpaceID]*userSpaceLoadInfo), } spaceIDs, err := t.db.UserSpace().GetAllIDs(t.db.DefCtx()) if err != nil { log.Warnf("get user space ids: %v", err) return } spaces := t.spaceMeta.GetMany(spaceIDs) for _, space := range spaces { if space == nil { continue } ctx.allUserSpaces[space.UserSpace.UserSpaceID] = &userSpaceLoadInfo{ UserSpace: space, } } lastPkgID := clitypes.PackageID(0) for { pkgs, err := db.DoTx21(t.db, t.db.Package().BatchGetDetailPaged, lastPkgID, BatchGetPackageDetailCount) if err != nil { log.Warnf("get package details: %v", err) return } if len(pkgs) == 0 { break } lastPkgID = pkgs[len(pkgs)-1].Package.PackageID for _, p := range pkgs { err := j.changeOne(ctx, p) if err != nil { log.Warnf("change redundancy: %v", err) return } } } } type changeRedundancyContext struct { ticktock *TickTock allUserSpaces map[clitypes.UserSpaceID]*userSpaceLoadInfo mostBlockStgIDs []clitypes.UserSpaceID } type userSpaceLoadInfo struct { UserSpace *clitypes.UserSpaceDetail AccessAmount float64 } func (j *ChangeRedundancy) changeOne(ctx *changeRedundancyContext, pkg clitypes.PackageDetail) error { log := logger.WithType[ChangeRedundancy]("TickTock") db2 := ctx.ticktock.db // allUserSpaces是复用的,所以需要先清空 for _, space := range ctx.allUserSpaces { space.AccessAmount = 0 } pkgAccessStats, err := db2.PackageAccessStat().GetByPackageID(db2.DefCtx(), pkg.Package.PackageID) if err != nil { return fmt.Errorf("get package access stats: %w", err) } for _, stat := range pkgAccessStats { info, ok := ctx.allUserSpaces[stat.UserSpaceID] if !ok { continue } info.AccessAmount = stat.Amount } lastObjID := clitypes.ObjectID(0) for { objs, err := db.DoTx31(db2, db2.Object().BatchGetDetailsPaged, pkg.Package.PackageID, lastObjID, BatchGetObjectDetailCount) if err != nil { return fmt.Errorf("get object details: %w", err) } if len(objs) == 0 { break } lastObjID = objs[len(objs)-1].Object.ObjectID reen := ctx.ticktock.pubLock.BeginReentrant() var allUpdatings []db.UpdatingObjectRedundancy var allSysEvts []datamap.SysEventBody ctx.mostBlockStgIDs = j.summaryRepObjectBlockUserSpaces(ctx, objs, 2) var willShrinks []clitypes.ObjectDetail for _, obj := range objs { newRed, selectedSpaces := j.chooseRedundancy(ctx, obj) // 冗余策略不需要调整,就检查是否需要收缩 if newRed == nil { willShrinks = append(willShrinks, obj) continue } reqBlder := reqbuilder.NewBuilder() for _, space := range selectedSpaces { reqBlder.Shard().Buzy(space.UserSpace.UserSpace.UserSpaceID) } err := reen.Lock(reqBlder.Build()) if err != nil { log.WithField("ObjectID", obj.Object.ObjectID).Warnf("acquire lock: %s", err.Error()) continue } updating, evt, err := j.doChangeRedundancy(ctx, obj, newRed, selectedSpaces) if updating != nil { allUpdatings = append(allUpdatings, *updating) } if evt != nil { allSysEvts = append(allSysEvts, evt) } if err != nil { log.WithField("ObjectID", obj.Object.ObjectID).Warnf("%s, its redundancy wont be changed", err.Error()) continue } } udpatings, sysEvts, err := j.doRedundancyShrink(ctx, pkg, willShrinks, reen) if err != nil { log.Warnf("redundancy shrink: %s", err.Error()) } else { allUpdatings = append(allUpdatings, udpatings...) allSysEvts = append(allSysEvts, sysEvts...) } if len(allUpdatings) > 0 { err := db.DoTx10(db2, db2.Object().BatchUpdateRedundancy, allUpdatings) if err != nil { reen.Unlock() log.Warnf("update object redundancy: %s", err.Error()) return err } } reen.Unlock() for _, e := range allSysEvts { ctx.ticktock.evtPub.Publish(e) } } return nil }