/* * Copyright 2015 LinkedIn Corp. * * 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 azkaban.executor.selector; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; import azkaban.executor.ExecutableFlow; import azkaban.executor.Executor; import azkaban.executor.ExecutorInfo; /** * De-normalized version of the candidateFilter, which also contains the implementation of the factor filters. * */ public final class ExecutorFilter extends CandidateFilter<Executor, ExecutableFlow> { private static Map<String, FactorFilter<Executor, ExecutableFlow>> filterRepository = null; /** * Gets the name list of all available filters. * @return the list of the names. * */ public static Set<String> getAvailableFilterNames(){ return filterRepository.keySet(); } // factor filter names. private static final String STATICREMAININGFLOWSIZE_FILTER_NAME = "StaticRemainingFlowSize"; private static final String MINIMUMFREEMEMORY_FILTER_NAME = "MinimumFreeMemory"; private static final String CPUSTATUS_FILTER_NAME = "CpuStatus"; /**<pre> * static initializer of the class. * We will build the filter repository here. * when a new filter is added, please do remember to register it here. * </pre> * */ static { filterRepository = new HashMap<>(); filterRepository.put(STATICREMAININGFLOWSIZE_FILTER_NAME, getStaticRemainingFlowSizeFilter()); filterRepository.put(MINIMUMFREEMEMORY_FILTER_NAME, getMinimumReservedMemoryFilter()); filterRepository.put(CPUSTATUS_FILTER_NAME, getCpuStatusFilter()); } /** * constructor of the ExecutorFilter. * @param filterList the list of filter to be registered, the parameter must be a not-empty and valid list object. * */ public ExecutorFilter(Collection<String> filterList) { // shortcut if the filter list is invalid. A little bit ugly to have to throw in constructor. if (null == filterList || filterList.size() == 0){ logger.error("failed to initialize executor filter as the passed filter list is invalid or empty."); throw new IllegalArgumentException("filterList"); } // register the filters according to the list. for (String filterName : filterList){ if (filterRepository.containsKey(filterName)){ this.registerFactorFilter(filterRepository.get(filterName)); } else { logger.error(String.format("failed to initialize executor filter "+ "as the filter implementation for requested factor '%s' doesn't exist.", filterName)); throw new IllegalArgumentException("filterList"); } } } @Override public String getName() { return "ExecutorFilter"; } /**<pre> * function to register the static remaining flow size filter. * NOTE : this is a static filter which means the filter will be filtering based on the system standard which is not * Coming for the passed flow. * Ideally this filter will make sure only the executor hasn't reached the Max allowed # of executing flows. *</pre> * */ private static FactorFilter<Executor, ExecutableFlow> getStaticRemainingFlowSizeFilter(){ return FactorFilter.create(STATICREMAININGFLOWSIZE_FILTER_NAME, (filteringTarget, referencingObject) -> { if (null == filteringTarget){ logger.debug(String.format("%s : filtering out the target as it is null.", STATICREMAININGFLOWSIZE_FILTER_NAME)); return false; } ExecutorInfo stats = filteringTarget.getExecutorInfo(); if (null == stats) { logger.debug(String.format("%s : filtering out %s as it's stats is unavailable.", STATICREMAININGFLOWSIZE_FILTER_NAME, filteringTarget.toString())); return false; } return stats.getRemainingFlowCapacity() > 0 ; }); } /**<pre> * function to register the static Minimum Reserved Memory filter. * NOTE : this is a static filter which means the filter will be filtering based on the system standard which is not * Coming for the passed flow. * This filter will filter out any executors that has the remaining memory below 6G *</pre> * */ private static FactorFilter<Executor, ExecutableFlow> getMinimumReservedMemoryFilter(){ return FactorFilter.create(MINIMUMFREEMEMORY_FILTER_NAME, new FactorFilter.Filter<Executor, ExecutableFlow>() { private static final int MINIMUM_FREE_MEMORY = 6 * 1024; @Override public boolean filterTarget(Executor filteringTarget, ExecutableFlow referencingObject) { if (null == filteringTarget){ logger.debug(String.format("%s : filtering out the target as it is null.", MINIMUMFREEMEMORY_FILTER_NAME)); return false; } ExecutorInfo stats = filteringTarget.getExecutorInfo(); if (null == stats) { logger.debug(String.format("%s : filtering out %s as it's stats is unavailable.", MINIMUMFREEMEMORY_FILTER_NAME, filteringTarget.toString())); return false; } return stats.getRemainingMemoryInMB() > MINIMUM_FREE_MEMORY ; } }); } /** * <pre> * function to register the static Minimum Reserved Memory filter. * NOTE : this is a static filter which means the filter will be filtering based on the system standard which * is not Coming for the passed flow. * This filter will filter out any executors that the current CPU usage exceed 95% * </pre> * */ private static FactorFilter<Executor, ExecutableFlow> getCpuStatusFilter(){ return FactorFilter.create(CPUSTATUS_FILTER_NAME, new FactorFilter.Filter<Executor, ExecutableFlow>() { private static final int MAX_CPU_CURRENT_USAGE = 95; @Override public boolean filterTarget(Executor filteringTarget, ExecutableFlow referencingObject) { if (null == filteringTarget){ logger.debug(String.format("%s : filtering out the target as it is null.", CPUSTATUS_FILTER_NAME)); return false; } ExecutorInfo stats = filteringTarget.getExecutorInfo(); if (null == stats) { logger.debug(String.format("%s : filtering out %s as it's stats is unavailable.", CPUSTATUS_FILTER_NAME, filteringTarget.toString())); return false; } return stats.getCpuUsage() < MAX_CPU_CURRENT_USAGE ; } }); } }