@Override public void register(ScalingConfiguration scalingConfiguration) { scalingConfigurationMap.put(scalingConfiguration.getId(), scalingConfiguration); }
private boolean validateMetric(final ScalingConfiguration s) { boolean ret = false; if ( metricNames.contains(s.getWorkloadMetric()) ) { ret = true; } else { LOG.warn("Service {} invalid: requested unknown workload metric {}", s.getId(), s.getWorkloadMetric()); } return ret; }
/** * Given a set of ScalingConfiguration objects, return the valid and applicable ones. * To be valid, a service must have the same application, and must pass all the * validation annotations present in ScalingConfiguration. * @param input a set of ScalingConfiguration objects, generally from a ServiceSource * @return the validated and applicable ScalingConfiguration objects */ public Set<ScalingConfiguration> getValidatedServices(final Set<ScalingConfiguration> input) { Set<ScalingConfiguration> ret = new HashSet<>(); for ( ScalingConfiguration s : input ) { LOG.debug("Source reported service {}", s); if ( validateMetric(s) ) { Set<ConstraintViolation<ScalingConfiguration>> violations = validator.validate(s); if ( violations.isEmpty() ) { LOG.debug("Service {} valid", s); ret.add(s); } else { LOG.warn("Service {} invalid: {}", s.getId(), violations); } } } return ret; }
/** * Schedule or reschedule a service for workload analysis and scaling. * @param config the ScalingConfiguration that describes a service to monitor and scale * @param analyser the WorkloadAnalyser that will be used to monitor and scale the service * @param initialDelay the initial delay before the scheduled thread kicks off */ private void scheduleOrReschedule(final ScalingConfiguration config, final WorkloadAnalyser analyser, final int initialDelay, final Alerter alerter) { LOG.debug("Scheduling service {}", config.getId()); // if we're already monitoring this service (ie. we're updating it), cancel the current ScalerThread if ( scheduledServices.containsKey(config.getId()) ) { cancel(config.getId()); } governor.register(config); ScheduledFuture future = scheduler.scheduleWithFixedDelay(new ScalerThread(governor, analyser, scaler, config.getId(), config.getMinInstances(), config.getMaxInstances(), config.getBackoffAmount(), alerter, resourceConfig), initialDelay, config.getInterval(), TimeUnit.SECONDS); scheduledServices.put(config.getId(), new ScheduledScalingService(config, future)); }
/** * Reload available services to monitor. * Firstly, get all available services from the ServiceSource, pass them to a ServiceValidator to determine the ones to track, * and then determine changes. Ones that are no longer present are cancelled from the scheduler. New ones or updated ones * are scheduled or rescheduled as appropriate. There is an initial scaling delay that increases, so that we don't try and * perform all the workload analysis and scaling calls at the same time. */ public void updateServices(final Set<ScalingConfiguration> sourceServices) { servicesLock.lock(); try { LOG.debug("Reloading services"); Set<ScalingConfiguration> acquired = validator.getValidatedServices(sourceServices); Map<String, ScalingConfiguration> acquiredMap = acquired.stream().collect(toMap(ScalingConfiguration::getId, c -> c)); getServicesToCancel(scheduledServices.keySet(), acquiredMap).forEach(this::cancel); int delay = 0; final Alerter alerter = new Alerter(alertDispatchers, alertConfig); for ( ScalingConfiguration s : getServicesToSchedule(scheduledServices, acquiredMap) ) { try { scheduleOrReschedule(s, getAnalyser(s), INITIAL_SCALING_DELAY + delay++, alerter); } catch (ScalerException e) { LOG.error("Failed to schedule service {}", s.getId(), e); cancel(s.getId()); } } } finally { servicesLock.unlock(); } }