public static ScaleAction ScalingLogic( [QueueTrigger("Metrics")] Metric metric, [Table("Scaling", "{ResourceName}", "{Name}")] ScalingStateEntity stateEntity, [Table("Scaling", "{ResourceName}", "{Name}")] out ScalingStateEntity newStateEntity, TraceWriter log) { // 1. Deserialize state var state = stateEntity?.SerializedState != null ? JsonConvert.DeserializeObject <ScalingState>(stateEntity.SerializedState) : new ScalingState(); var history = state.History; log.Info($"Scaling logic: Received {metric.Name}, previous state is {string.Join(", ", history)}"); // 2. Add current metric value, remove old values history.Add(metric.Value); history.RemoveAll(e => e.Time < metric.Value.Time.Subtract(period)); // 3. Compare the aggregates to thresholds, produce scaling action if needed ScaleAction action = null; if (history.Count >= 5 && DateTime.Now - state.LastScalingActionTime > cooldownPeriod) { var average = (int)history.Average(e => e.Value); var maximum = (int)history.Max(e => e.Value); if (average > thresholdUp) { log.Info($"Scaling logic: Value {average} is too high, scaling {metric.ResourceName} up..."); state.LastScalingActionTime = DateTime.Now; action = new ScaleAction(metric.ResourceName, ScaleActionType.Up); } else if (maximum < thresholdDown) { log.Info($"Scaling logic: Value {maximum} is low, scaling {metric.ResourceName} down..."); state.LastScalingActionTime = DateTime.Now; action = new ScaleAction(metric.ResourceName, ScaleActionType.Down); } } // 4. Serialize the state back and return the action newStateEntity = stateEntity != null ? stateEntity : new ScalingStateEntity { PartitionKey = metric.ResourceName, RowKey = metric.Name }; newStateEntity.SerializedState = JsonConvert.SerializeObject(state); return(action); }
public static void Scaler([QueueTrigger("Actions")] ScaleAction action, TraceWriter log) { log.Info($"Scaler executed at: {DateTime.Now}"); var secrets = Environment.GetEnvironmentVariable("ServicePrincipal").Split(','); var credentials = SdkContext.AzureCredentialsFactory .FromServicePrincipal(secrets[0], secrets[1], secrets[2], AzureEnvironment.AzureGlobalCloud); var azure = Azure.Configure() .Authenticate(credentials) .WithDefaultSubscription(); var plan = azure.AppServices .AppServicePlans .List() .First(p => p.Name.Contains(action.ResourceName)); var newCapacity = action.Type == ScaleActionType.Down ? plan.Capacity - 1 : plan.Capacity + 1; log.Info($"Scaler: Switching {action.ResourceName} from {plan.Capacity} {action.Type} to {newCapacity}"); plan.Update() .WithCapacity(newCapacity) .Apply(); }