Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<Import Project="../common.props" />

<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netcoreapp3.1</TargetFrameworks>
</PropertyGroup>

<!-- NuGet package settings -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,24 @@ public override bool TryGetScaleMonitor(
string storageConnectionString,
out IScaleMonitor scaleMonitor)
{
scaleMonitor = this.scaleMonitor ??= new SqlScaleMonitor(this.service, hubName);
SqlMetricsProvider sqlMetricsProvider = new SqlMetricsProvider(this.service);
scaleMonitor = this.scaleMonitor ??= new SqlScaleMonitor(this.service, hubName, sqlMetricsProvider);
return true;
}

#if NETCOREAPP
Comment thread
cgillum marked this conversation as resolved.
Outdated
public override bool TryGetTargetScaler(
string functionId,
string functionName,
string hubName,
string connectionName,
out ITargetScaler targetScaler)
{
SqlMetricsProvider sqlMetricsProvider = new SqlMetricsProvider(this.service);
targetScaler = new SqlTargetScaler(functionId, sqlMetricsProvider);

return true;
}
#endif
}
}
29 changes: 29 additions & 0 deletions src/DurableTask.SqlServer.AzureFunctions/SqlMetricsProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace DurableTask.SqlServer.AzureFunctions
{
using System.Threading;
using System.Threading.Tasks;

public class SqlMetricsProvider
{
readonly SqlOrchestrationService service;

public SqlMetricsProvider(SqlOrchestrationService service, int? previousWorkerCount = null)
Comment thread
bachuv marked this conversation as resolved.
Outdated
Comment thread
cgillum marked this conversation as resolved.
Outdated
{
this.service = service;
}

public virtual async Task<SqlScaleMetric> GetMetricsAsync(int? previousWorkerCount = null)
{
// GetRecommendedReplicaCountAsync will write a trace if the recommendation results
// in a worker count that is different from the worker count we pass in as an argument.
int recommendedReplicaCount = await this.service.GetRecommendedReplicaCountAsync(
previousWorkerCount,
CancellationToken.None);

return new SqlScaleMetric { RecommendedReplicaCount = recommendedReplicaCount };
Comment thread
bachuv marked this conversation as resolved.
}
}
}
2 changes: 1 addition & 1 deletion src/DurableTask.SqlServer.AzureFunctions/SqlScaleMetric.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace DurableTask.SqlServer.AzureFunctions
{
using Microsoft.Azure.WebJobs.Host.Scale;

class SqlScaleMetric : ScaleMetrics
public class SqlScaleMetric : ScaleMetrics
{
public int RecommendedReplicaCount { get; set; }
}
Expand Down
12 changes: 4 additions & 8 deletions src/DurableTask.SqlServer.AzureFunctions/SqlScaleMonitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ class SqlScaleMonitor : IScaleMonitor<SqlScaleMetric>
static readonly ScaleStatus ScaleOutVote = new ScaleStatus { Vote = ScaleVote.ScaleOut };

readonly SqlOrchestrationService service;
readonly SqlMetricsProvider metricsProvider;

int? previousWorkerCount = -1;

public SqlScaleMonitor(SqlOrchestrationService service, string taskHubName)
public SqlScaleMonitor(SqlOrchestrationService service, string taskHubName, SqlMetricsProvider sqlMetricsProvider)
{
this.service = service ?? throw new ArgumentNullException(nameof(service));
this.Descriptor = new ScaleMonitorDescriptor($"DurableTask-SqlServer:{taskHubName ?? "default"}");
this.metricsProvider = sqlMetricsProvider ?? throw new ArgumentNullException(nameof(sqlMetricsProvider));
}

/// <inheritdoc />
Expand All @@ -38,13 +40,7 @@ public SqlScaleMonitor(SqlOrchestrationService service, string taskHubName)
/// <inheritdoc />
public async Task<SqlScaleMetric> GetMetricsAsync()
{
// GetRecommendedReplicaCountAsync will write a trace if the recommendation results
// in a worker count that is different from the worker count we pass in as an argument.
int recommendedReplicaCount = await this.service.GetRecommendedReplicaCountAsync(
this.previousWorkerCount,
CancellationToken.None);

return new SqlScaleMetric { RecommendedReplicaCount = recommendedReplicaCount };
return await this.metricsProvider.GetMetricsAsync(this.previousWorkerCount);
}

/// <inheritdoc />
Expand Down
32 changes: 32 additions & 0 deletions src/DurableTask.SqlServer.AzureFunctions/SqlTargetScaler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace DurableTask.SqlServer.AzureFunctions
{
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Scale;

#if !NETSTANDARD
public class SqlTargetScaler : ITargetScaler
{
readonly SqlMetricsProvider sqlMetricsProvider;
readonly TargetScalerResult scaleResult;
Comment thread
cgillum marked this conversation as resolved.
Outdated

public SqlTargetScaler(string functionId, SqlMetricsProvider sqlMetricsProvider)
{
this.sqlMetricsProvider = sqlMetricsProvider;
this.scaleResult = new TargetScalerResult();
this.TargetScalerDescriptor = new TargetScalerDescriptor(functionId);
}

public TargetScalerDescriptor TargetScalerDescriptor { get; private set; }
Comment thread
cgillum marked this conversation as resolved.
Outdated

public async Task<TargetScalerResult> GetScaleResultAsync(TargetScalerContext context)
{
SqlScaleMetric sqlScaleMetric = await this.sqlMetricsProvider.GetMetricsAsync();
this.scaleResult.TargetWorkerCount = sqlScaleMetric.RecommendedReplicaCount;
return this.scaleResult;
}
}
#endif
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace DurableTask.SqlServer.AzureFunctions.Tests
{
using System;
using DurableTask.Core;
using Microsoft.Azure.WebJobs.Extensions.DurableTask;
using Microsoft.Azure.WebJobs.Host.Scale;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;
using Xunit.Abstractions;

public class TargetBasedScalingTests
{
readonly Mock<SqlMetricsProvider> metricsProviderMock;
readonly Mock<IOrchestrationService> orchestrationServiceMock;

public TargetBasedScalingTests()
{
this.orchestrationServiceMock = new Mock<IOrchestrationService>(MockBehavior.Strict);

this.metricsProviderMock = new Mock<SqlMetricsProvider>(
MockBehavior.Strict,
null,
null);
Comment thread
cgillum marked this conversation as resolved.
Outdated
}

[Theory]
[InlineData(0)]
[InlineData(10)]
[InlineData(20)]
public async void TargetBasedScalingTest(int expectedTargetWorkerCount)
Comment thread
cgillum marked this conversation as resolved.
{
var durabilityProviderMock = new Mock<DurabilityProvider>(
MockBehavior.Strict,
"storageProviderName",
this.orchestrationServiceMock.Object,
new Mock<IOrchestrationServiceClient>().Object,
"connectionName");

SqlScaleMetric sqlScaleMetric = new SqlScaleMetric()
{
RecommendedReplicaCount = expectedTargetWorkerCount,
};

this.metricsProviderMock.Setup(m => m.GetMetricsAsync(null)).ReturnsAsync(sqlScaleMetric);

SqlTargetScaler targetScaler = new SqlTargetScaler(
"functionId",
this.metricsProviderMock.Object);

TargetScalerResult result = await targetScaler.GetScaleResultAsync(new TargetScalerContext());

Assert.Equal(expectedTargetWorkerCount, result.TargetWorkerCount);
}
}
}