// 检查App间的冲突 public bool IsConflictWith(AppInst inst) { var appB = inst.App; var appBCnt = AppCountKv.GetValueOrDefault(appB, defaultValue: 0); foreach (var kv in AppCountKv) { //<appA, appB, bLimit> var appA = kv.Key; //appA是已部署的应用 if (appA == null) { continue; } var bLimit = appA.XLimit(appB.Id); if (appBCnt + 1 > bLimit) { return(true); } //同时,已部署的应用不会与将要部署的inst的规则冲突 //<appB, appA, aLimit> var aLimit = appB.XLimit(appA.Id); var appACnt = kv.Value; if (appACnt > aLimit) { return(true); } } return(false); }
// 假设从机器上移除 instOld 之后, 检查 instNew 是否有亲和冲突 // 注意参数顺序 private static bool HasConflict(AppInst instOld, AppInst instNew) { var m = instOld.Machine; var appCountKv = m.AppCountKv; //直接修改 var appOld = instOld.App; //appOld的所有实例都在之前的循环move到别的机器了 if (!appCountKv.ContainsKey(appOld)) { return(false); } var appOldCnt = appCountKv[appOld]; if (appOldCnt == 1) { appCountKv.Remove(appOld); } else { appCountKv[appOld] = appOldCnt - 1; } var result = m.IsConflictWith(instNew); appCountKv[appOld] = appOldCnt; //恢复原状 return(result); }
//将inst移动到mDest,如果移动失败,deta为正数,或double.MaxValue //TODO: Move到空机器 private static bool TryMove(Machine mDest, AppInst inst, out double delta) { delta = double.MaxValue; if (!mDest.HasApp || !mDest.CanPut(inst)) { return(false); } var mSrc = inst.Machine; var scoreBefore = mSrc.Score + mDest.Score; mDest.TryPut(inst, ignoreCheck: true); var scoreAfter = mSrc.Score + mDest.Score; delta = scoreAfter - scoreBefore; if (delta > 0.0 // delta == 0.0 && scoreAfter == 2.0 // 即 move 前后两个机器的 cpu util 均小于 0.5,需要 move // ReSharper disable once CompareOfFloatsByEqualityOperator || delta == 0.0 && scoreAfter > 2.0 || delta < 0.0 && delta > -0.00001) { mSrc.TryPut(inst, ignoreCheck: true); //恢复原状 } return(delta <= 0.0); }
// 检查当前累积使用的资源量 usage **加上 r 之后** 是否会超出 capacity, // 不会修改当前资源量 public bool IsOverCapacityWith(AppInst inst, double cpuUtilLimit = 1.0) { var r = inst.R; return(_usage.Disk + r.Disk > CapDisk || _usage.P + r.P > Capacity.P || _usage.Pm + r.Pm > Capacity.Pm || _usage.M + r.M > Capacity.M || _usage.Cpu.MaxWith(r.Cpu) > CapCpu * cpuUtilLimit || //TODO: Round? _usage.Mem.MaxWith(r.Mem) > CapMem); }
public void Remove(AppInst inst, // 兼容:如果从是 PrevMachine 调用的,则不修改 Machine及Deployed 字段,仅扣减资源和相关计数 bool setDeployFlag = true) { // if (!AppInstSet.Remove(inst)) { return; } AppInstCount -= 1; HasApp = AppInstCount != 0; _usage.Subtract(inst.R); _score = double.MinValue; _avail.Invalid(); _xUsage.Invalid(); AppCountKv[inst.App] -= 1; if (AppCountKv[inst.App] == 0) { AppCountKv.Remove(inst.App); AppKv.Remove(inst.App); } else if (AppKv[inst.App] == inst) { //要移除的 inst 恰好是该类 App 的代表,移除后需要找一个替补 //因为计数不为0,肯定存在替补 var found = false; foreach (var i in AppInstSet) { if (i.App == inst.App) { AppKv[i.App] = i; found = true; break; } } if (!found) { throw new Exception($"[RemoveInst]: AppKv cannot find a substitution for {inst}"); } } // 兼容:如果是从 PrevMachine 调用的,不修改下面这两个字段 if (!setDeployFlag) { return; } inst.Machine = null; inst.IsDeployed = false; }
//如果交换失败,deta为正数,或double.MaxValue //引入u1,u2,diff,shrink这几个参数是为了减少GC private static bool TrySwap(AppInst inst1, AppInst inst2, Resource u1, Resource u2, Resource diff, Resource shrink, out double delta) { var m1 = inst1.Machine; var m2 = inst2.Machine; diff.DiffOf(inst1.R, inst2.R); delta = double.MaxValue; m1.Usage.ShrinkTo(shrink); u1.DiffOf(shrink, diff); if (u1.AnyLargerThan(m1.Capacity)) { return(false); } m2.Usage.ShrinkTo(shrink); u2.SumOf(shrink, diff); if (u2.AnyLargerThan(m2.Capacity)) { return(false); } m1.Usage.Cpu.ShrinkTo(shrink.Cpu); var scoreBefore = shrink.Cpu.Score(m1.CapCpu, m1.AppInstCount); m2.Usage.Cpu.ShrinkTo(shrink.Cpu); scoreBefore += shrink.Cpu.Score(m2.CapCpu, m2.AppInstCount); delta = u1.Cpu.Score(m1.CapCpu, m1.AppInstCount) + u2.Cpu.Score(m2.CapCpu, m2.AppInstCount) - scoreBefore; //期望delta是负数,且绝对值越大越好 if (delta >= 0.0 || delta < 0.0 && delta > -0.00001) { return(false); } if (HasConflict(inst1, inst2) || HasConflict(inst2, inst1)) { return(false); } m1 = inst1.Machine; m2 = inst2.Machine; m1.TryPut(inst2, ignoreCheck: true); m2.TryPut(inst1, ignoreCheck: true); return(true); }
// 读取实例,保持原有顺序! private void ReadAppInst(string csv) { var i = 0; Util.ReadCsv(csv, parts => { var instId = parts[0].Id(); var appId = parts[1].Id(); var app = DataSet.AppKv[appId]; var inst = new AppInst(instId, app); app.InstCount++; AppInsts[i++] = inst; } ); }
// 如果添加成功,会自动从旧机器上迁移过来(如果有的话) public bool TryPut(AppInst inst, double cpuUtilLimit = 1.0, bool ignoreCheck = false, bool autoRemove = true) // 兼容:分轮次迁移不自动迁移 { if (AppInstSet.Contains(inst)) { return(true); //已经存在inst了,幂等 } if (!ignoreCheck && !CanPut(inst, cpuUtilLimit)) { return(false); } if (!AppInstSet.Add(inst)) { throw new Exception($"[TryPut]: {inst}"); } AppInstCount += 1; HasApp = true; _usage.Add(inst.R); _score = double.MinValue; _avail.Invalid(); _xUsage.Invalid(); AppCountKv[inst.App] = AppCountKv.GetValueOrDefault(inst.App, defaultValue: 0) + 1; // 每类App只需保存一个inst作为代表即可 AppKv.TryAdd(inst.App, inst); if (autoRemove) { inst.Machine?.Remove(inst); } else { inst.PrevMachine = inst.Machine; } inst.Machine = this; inst.IsDeployed = true; return(true); }
public string FailureMsg(AppInst inst) { return($"inst_{inst.Id},m_{Id}" + $"{(IsOverCapacityWith(inst) ? ",R" : "")}" + $"{(IsConflictWith(inst) ? ",X" : "")}"); }
public bool CanPut(AppInst inst, double cpuUtilLimit = 1.0) { return(!IsOverCapacityWith(inst, cpuUtilLimit) && !IsConflictWith(inst)); }