All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.fenzo.PreferentialNamedConsumableResourceSet Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
/*
 * Copyright 2015 Netflix, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.netflix.fenzo;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * This encapsulates preferential resource sets available on a VM. Resource sets are two level resources that can
 * be assigned to tasks that specify a name to reserve for an available resource set, and the number of sub-resources
 * (second level of the two level resource) it needs.
 * 

A {@link PreferentialNamedConsumableResourceSet} contains 1 or more resource sets, * {@link com.netflix.fenzo.PreferentialNamedConsumableResourceSet.PreferentialNamedConsumableResource}, each of which * can be assigned (or reserved to) a name requested by tasks, if currently unassigned. Each resource set contains one * or more count of resources available for assignment to tasks. *

A task can be assigned to one of the resource sets if it either has no tasks assigned to it, or the name assigned * to the resource set matches what the task being assigned is requesting. The number of sub-resources requested by task * is also checked. Tasks may request 0 or more sub-resources. The assignment attempts to use as few resource sets as * possible and returns a fitness score that helps scheduler pick between multiple VMs that can potentially fit the task. * The resulting assignment contains the index of the resource set assigned. The resource sets are assigned indexes * starting with 0. */ public class PreferentialNamedConsumableResourceSet { final static String attributeName = "ResourceSet"; private static String getResNameVal(String name, TaskRequest request) { final Map customNamedResources = request.getCustomNamedResources(); if(customNamedResources!=null) { final TaskRequest.NamedResourceSetRequest setRequest = customNamedResources.get(name); return setRequest==null? CustomResAbsentKey : setRequest.getResValue(); } return CustomResAbsentKey; } public static class ConsumeResult { private final int index; private final String attrName; private final String resName; private final double fitness; @JsonCreator @JsonIgnoreProperties(ignoreUnknown=true) public ConsumeResult(@JsonProperty("index") int index, @JsonProperty("attrName") String attrName, @JsonProperty("resName") String resName, @JsonProperty("fitness") double fitness) { this.index = index; this.attrName = attrName; this.resName = resName; this.fitness = fitness; } public int getIndex() { return index; } public String getAttrName() { return attrName; } public String getResName() { return resName; } public double getFitness() { return fitness; } } public static class PreferentialNamedConsumableResource { private final double maxFitness; private final int index; private final String attrName; private String resName=null; private final int limit; private final Map usageBy; private int usedSubResources=0; PreferentialNamedConsumableResource(int i, String attrName, int limit) { this.index = i; this.attrName = attrName; this.limit = limit; usageBy = new HashMap<>(); // we add 1.0 to max fitness possible since we add 1.0 for the situation when there is already a task // assigned with the same resValue even though it uses 0.0 subResources, versus, there are no assignments yet. maxFitness = limit + 1.0; } public int getIndex() { return index; } public String getResName() { return resName; } public int getLimit() { return limit; } public Map getUsageBy() { return usageBy; } double getUsedCount() { if(resName==null) return -1; return usedSubResources; } double getFitness(TaskRequest request) { String r = getResNameVal(attrName, request); if(resName == null) return 0.5 / maxFitness; // unassigned: 0.0 indicates no fitness, so return 0.5, which is less than // the case of assigned with 0 sub-resources if(!resName.equals(r)) return 0.0; final TaskRequest.NamedResourceSetRequest setRequest = request.getCustomNamedResources()==null? null : request.getCustomNamedResources().get(attrName); double subResNeed = setRequest==null? 0.0 : setRequest.getNumSubResources(); if(usedSubResources + subResNeed > limit) return 0.0; return Math.min(1.0, usedSubResources + subResNeed + 1.0 / maxFitness); } void consume(TaskRequest request) { String r = getResNameVal(attrName, request); consume(r, request); } void consume(String assignedResName, TaskRequest request) { if(usageBy.get(request.getId()) != null) return; // already consumed if(resName!=null && !resName.equals(assignedResName)) throw new IllegalStateException(this.getClass().getName() + " already consumed by " + resName + ", can't consume for " + assignedResName); if(resName == null) { resName = assignedResName; usageBy.clear(); } final TaskRequest.NamedResourceSetRequest setRequest = request.getCustomNamedResources()==null? null : request.getCustomNamedResources().get(attrName); double subResNeed = setRequest==null? 0.0 : setRequest.getNumSubResources(); if(usedSubResources + subResNeed > limit) throw new RuntimeException(this.getClass().getName() + " already consumed for " + resName + " up to the limit of " + limit); usageBy.put(request.getId(), setRequest); usedSubResources += subResNeed; } boolean release(TaskRequest request) { String r = getResNameVal(attrName, request); if(resName != null && !resName.equals(r)) { return false; } final TaskRequest.NamedResourceSetRequest removed = usageBy.remove(request.getId()); if(removed == null) return false; usedSubResources -= removed.getNumSubResources(); if(usageBy.isEmpty()) resName = null; return true; } } public static final String CustomResAbsentKey = "CustomResAbsent"; private final String name; private final List usageBy; public PreferentialNamedConsumableResourceSet(String name, int val0, int val1) { this.name = name; usageBy = new ArrayList<>(val0); for(int i=0; i consumedNamedResources = assignedResources.getConsumedNamedResources(); if(consumedNamedResources!=null && !consumedNamedResources.isEmpty()) { for(PreferentialNamedConsumableResourceSet.ConsumeResult consumeResult: consumedNamedResources) { if(name.equals(consumeResult.getAttrName())) { final int index = consumeResult.getIndex(); if(index < 0 || index > usageBy.size()) throw new IllegalStateException("Illegal assignment of namedResource " + name + ": has " + usageBy.size() + " resource sets, can't assign to index " + index ); usageBy.get(index).consume(consumeResult.getResName(), request); } } } } } // returns 0.0 for no fitness at all, or <=1.0 for fitness double getFitness(TaskRequest request) { return consumeIntl(request, true).fitness; } private ConsumeResult consumeIntl(TaskRequest request, boolean skipConsume) { PreferentialNamedConsumableResource best = null; double bestFitness=0.0; for(PreferentialNamedConsumableResource r: usageBy) { double f = r.getFitness(request); if(f == 0.0) continue; if(bestFitness < f) { best = r; bestFitness = f; } } if(!skipConsume) { if (best == null) throw new RuntimeException("Unexpected to have no availability for job " + request.getId() + " for consumable resource " + name); best.consume(request); } return new ConsumeResult( best==null? -1 : best.index, best==null? null : best.attrName, best==null? null : best.resName, bestFitness ); } boolean release(TaskRequest request) { for(PreferentialNamedConsumableResource r: usageBy) if(r.release(request)) return true; return false; } int getNumSubResources() { return usageBy.get(0).getLimit()-1; } List getUsedCounts() { List counts = new ArrayList<>(usageBy.size()); for(PreferentialNamedConsumableResource r: usageBy) counts.add(r.getUsedCount()); return counts; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy