@Override public RLESparseResourceAllocation availableResources( RLESparseResourceAllocation available, Plan plan, String user, ReservationId oldId, long start, long end) throws PlanningException { // this only propagates the instantaneous maxInst properties, while // the time-varying one depends on the current allocation as well // and are not easily captured here Resource planTotalCapacity = plan.getTotalCapacity(); Resource maxInsRes = Resources.multiply(planTotalCapacity, maxInst); NavigableMap<Long, Resource> instQuota = new TreeMap<Long, Resource>(); instQuota.put(start, maxInsRes); RLESparseResourceAllocation instRLEQuota = new RLESparseResourceAllocation(instQuota, plan.getResourceCalculator()); RLESparseResourceAllocation used = plan.getConsumptionForUserOverTime(user, start, end); instRLEQuota = RLESparseResourceAllocation.merge( plan.getResourceCalculator(), planTotalCapacity, instRLEQuota, used, RLEOperator.subtract, start, end); instRLEQuota = RLESparseResourceAllocation.merge( plan.getResourceCalculator(), planTotalCapacity, available, instRLEQuota, RLEOperator.min, start, end); return instRLEQuota; }
@Override public void validate(Plan plan, ReservationAllocation reservation) throws PlanningException { // this is entire method invoked under a write-lock on the plan, no need // to synchronize accesses to the plan further // Try to verify whether there is already a reservation with this ID in // the system (remove its contribution during validation to simulate a // try-n-swap // update). ReservationAllocation oldReservation = plan.getReservationById(reservation.getReservationId()); // sanity check that the update of a reservation is not changing username if (oldReservation != null && !oldReservation.getUser().equals(reservation.getUser())) { throw new MismatchedUserException( "Updating an existing reservation with mismatched user:"******" != " + reservation.getUser()); } long startTime = reservation.getStartTime(); long endTime = reservation.getEndTime(); long step = plan.getStep(); Resource planTotalCapacity = plan.getTotalCapacity(); Resource maxAvgRes = Resources.multiply(planTotalCapacity, maxAvg); Resource maxInsRes = Resources.multiply(planTotalCapacity, maxInst); // define variable that will store integral of resources (need diff class to // avoid overflow issues for long/large allocations) IntegralResource runningTot = new IntegralResource(0L, 0L); IntegralResource maxAllowed = new IntegralResource(maxAvgRes); maxAllowed.multiplyBy(validWindow / step); RLESparseResourceAllocation userCons = plan.getConsumptionForUserOverTime( reservation.getUser(), startTime - validWindow, endTime + validWindow); // check that the resources offered to the user during any window of length // "validWindow" overlapping this allocation are within maxAllowed // also enforce instantaneous and physical constraints during this pass for (long t = startTime - validWindow; t < endTime + validWindow; t += step) { Resource currExistingAllocTot = plan.getTotalCommittedResources(t); Resource currExistingAllocForUser = userCons.getCapacityAtTime(t); Resource currNewAlloc = reservation.getResourcesAtTime(t); Resource currOldAlloc = Resources.none(); if (oldReservation != null) { currOldAlloc = oldReservation.getResourcesAtTime(t); } // throw exception if the cluster is overcommitted // tot_allocated - old + new > capacity Resource inst = Resources.subtract(Resources.add(currExistingAllocTot, currNewAlloc), currOldAlloc); if (Resources.greaterThan( plan.getResourceCalculator(), planTotalCapacity, inst, planTotalCapacity)) { throw new ResourceOverCommitException( " Resources at time " + t + " would be overcommitted (" + inst + " over " + plan.getTotalCapacity() + ") by accepting reservation: " + reservation.getReservationId()); } // throw exception if instantaneous limits are violated // tot_alloc_to_this_user - old + new > inst_limit if (Resources.greaterThan( plan.getResourceCalculator(), planTotalCapacity, Resources.subtract(Resources.add(currExistingAllocForUser, currNewAlloc), currOldAlloc), maxInsRes)) { throw new PlanningQuotaException( "Instantaneous quota capacity " + maxInst + " would be passed at time " + t + " by accepting reservation: " + reservation.getReservationId()); } // throw exception if the running integral of utilization over validWindow // is violated. We perform a delta check, adding/removing instants at the // boundary of the window from runningTot. // runningTot = previous_runningTot + currExistingAllocForUser + // currNewAlloc - currOldAlloc - pastNewAlloc - pastOldAlloc; // Where: // 1) currNewAlloc, currExistingAllocForUser represent the contribution of // the instant in time added in this pass. // 2) pastNewAlloc, pastOldAlloc are the contributions relative to time // instants that are being retired from the the window // 3) currOldAlloc is the contribution (if any) of the previous version of // this reservation (the one we are updating) runningTot.add(currExistingAllocForUser); runningTot.add(currNewAlloc); runningTot.subtract(currOldAlloc); // expire contributions from instant in time before (t - validWindow) if (t > startTime) { Resource pastOldAlloc = userCons.getCapacityAtTime(t - validWindow); Resource pastNewAlloc = reservation.getResourcesAtTime(t - validWindow); // runningTot = runningTot - pastExistingAlloc - pastNewAlloc; runningTot.subtract(pastOldAlloc); runningTot.subtract(pastNewAlloc); } // check integral // runningTot > maxAvg * validWindow // NOTE: we need to use comparator of IntegralResource directly, as // Resource and ResourceCalculator assume "int" amount of resources, // which is not sufficient when comparing integrals (out-of-bound) if (maxAllowed.compareTo(runningTot) < 0) { throw new PlanningQuotaException( "Integral (avg over time) quota capacity " + maxAvg + " over a window of " + validWindow / 1000 + " seconds, " + " would be passed at time " + t + "(" + new Date(t) + ") by accepting reservation: " + reservation.getReservationId()); } } }