示例#1
0
 private static void WriteMetrics(ILogger log, UsageInfo usageInfo, HyperScaleTier currentSlo, HyperScaleTier targetSlo)
 {
     log.LogMetric("DataPoints", usageInfo.DataPoints);
     log.LogMetric("AvgCpuPercent", Convert.ToDouble(usageInfo.AvgCpuPercent));
     log.LogMetric("MovingAvgCpuPercent", Convert.ToDouble(usageInfo.MovingAvgCpuPercent));
     log.LogMetric("CurrentCores", Convert.ToDouble(currentSlo.Cores));
     log.LogMetric("TargetCores", Convert.ToDouble(targetSlo.Cores));
 }
示例#2
0
        public static HyperScaleTier Parse(string tierName)
        {
            var curName = tierName.ToLower();
            var parts   = curName.Split('_');

            if (parts[0] != "hs")
            {
                throw new ArgumentException($"'{tierName}' is not an Hyperscale Tier");
            }

            var result = new HyperScaleTier();

            result.Generation = int.Parse(parts[1].Replace("gen", string.Empty));
            result.Cores      = int.Parse(parts[2]);

            return(result);
        }
示例#3
0
        public static HyperScaleTier GetServiceObjective(HyperScaleTier currentSLO, SearchDirection direction)
        {
            var targetSLO     = currentSLO;
            var availableSlos = HyperscaleSLOs[currentSLO.Generation];
            var index         = availableSlos.IndexOf(currentSLO.ToString());

            if (direction == SearchDirection.Next && index < availableSlos.Count)
            {
                targetSLO = HyperScaleTier.Parse(availableSlos[index + 1]);
            }

            if (direction == SearchDirection.Previous && index > 0)
            {
                targetSLO = HyperScaleTier.Parse(availableSlos[index - 1]);
            }

            return(targetSLO);
        }
示例#4
0
        public static void Run([TimerTrigger("*/5 * * * * *")] TimerInfo timer, ILogger log)
        {
            var autoscalerConfig = new AutoScalerConfiguration();

            string connectionString = Environment.GetEnvironmentVariable("AzureSQLConnection");
            string databaseName     = (new SqlConnectionStringBuilder(connectionString)).InitialCatalog;

            using (var conn = new SqlConnection(connectionString))
            {
                // Get usage data
                var followingRows = autoscalerConfig.RequiredDataPoints - 1;
                var usageInfo     = conn.QuerySingleOrDefault <UsageInfo>($@"
                    select top (1)
                        [end_time] as [TimeStamp], 
                        databasepropertyex(db_name(), 'ServiceObjective') as ServiceObjective,
                        [avg_cpu_percent] as AvgCpuPercent, 
                        avg([avg_cpu_percent]) over (order by end_time desc rows between current row and {followingRows} following) as MovingAvgCpuPercent,
                        count(*) over (order by end_time desc rows between current row and {followingRows} following) as DataPoints
                    from 
                        sys.dm_db_resource_stats
                    order by 
                        end_time desc 
                ");

                // If SLO is happening result could be null
                if (usageInfo == null)
                {
                    log.LogInformation("No information received from server.");
                    return;
                }

                // Decode current SLO
                var currentSlo = HyperScaleTier.Parse(usageInfo.ServiceObjective);
                var targetSlo  = currentSlo;

                // At least one minute of historical data is needed
                if (usageInfo.DataPoints < autoscalerConfig.RequiredDataPoints)
                {
                    log.LogInformation("Not enough data points.");
                    WriteMetrics(log, usageInfo, currentSlo, targetSlo);
                    conn.Execute("INSERT INTO [dbo].[AutoscalerMonitor] (RequestedSLO, UsageInfo) VALUES (NULL, @UsageInfo)", new { UsageInfo = JsonConvert.SerializeObject(usageInfo) });
                    return;
                }

                // Scale Up
                if (usageInfo.MovingAvgCpuPercent > autoscalerConfig.HighThreshold)
                {
                    targetSlo = GetServiceObjective(currentSlo, SearchDirection.Next);
                    if (targetSlo != null && currentSlo.Cores < autoscalerConfig.vCoreMax && currentSlo != targetSlo)
                    {
                        log.LogInformation($"HIGH threshold reached: scaling up to {targetSlo}");
                        conn.Execute($"alter database [{databaseName}] modify (service_objective = '{targetSlo}')");
                    }
                }

                // Scale Down
                if (usageInfo.MovingAvgCpuPercent < autoscalerConfig.LowThreshold)
                {
                    targetSlo = GetServiceObjective(currentSlo, SearchDirection.Previous);
                    if (targetSlo != null && currentSlo.Cores > autoscalerConfig.vCoreMin && currentSlo != targetSlo)
                    {
                        log.LogInformation($"LOW threshold reached: scaling down to {targetSlo}");
                        conn.Execute($"alter database [{databaseName}] modify (service_objective = '{targetSlo}')");
                    }
                }

                // Write current SLO to monitor table
                WriteMetrics(log, usageInfo, currentSlo, targetSlo);
                conn.Execute("INSERT INTO [dbo].[AutoscalerMonitor] (RequestedSLO, UsageInfo) VALUES (@RequestedSLO, @UsageInfo)", new { @RequestedSLO = targetSlo.ToString().ToUpper(), UsageInfo = JsonConvert.SerializeObject(usageInfo) });
            }
        }