Skip to content

Commit 0c5d3af

Browse files
committed
udpate by comments
1 parent 7ba35f6 commit 0c5d3af

File tree

9 files changed

+73
-71
lines changed

9 files changed

+73
-71
lines changed

.github/workflows/validate-build-scale.yml

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
name: Functions Scale Tests Azure Storage
2+
permissions:
3+
contents: read
24

35
on:
46
push:
@@ -35,7 +37,7 @@ jobs:
3537
run: dotnet restore $solution
3638

3739
- name: Build
38-
run: dotnet build $solution
40+
run: dotnet build $solution -c $config
3941

4042
- name: Set up Node.js (needed for Azurite)
4143
uses: actions/setup-node@v3
@@ -46,4 +48,18 @@ jobs:
4648
run: npm install -g azurite
4749

4850
- name: Run Scale tests
49-
run: azurite --silent --blobPort 10000 --queuePort 10001 --tablePort 10002 & dotnet test ./test/ScaleTests/Microsoft.Azure.WebJobs.Extensions.DurableTask.FunctionsScale.Tests.csproj --no-build --verbosity normal
51+
run : |
52+
azurite --silent --blobPort 10000 --queuePort 10001 --tablePort 10002
53+
54+
# Wait for Azurite to be ready
55+
for i in {1..30}; do
56+
if nc -z 127.0.0.1 10000; then
57+
echo "Azurite is ready"
58+
break
59+
fi
60+
echo "Azurite is not ready, waiting ($1/30)"
61+
sleep 1
62+
done
63+
64+
# Run tests
65+
dotnet test ./test/ScaleTests/Microsoft.Azure.WebJobs.Extensions.DurableTask.FunctionsScale.Tests.csproj -c $config --no-build --verbosity normal

src/Microsoft.Azure.WebJobs.Extensions.DurableTask.FunctionsScale/AzureStorage/AzureStorageScalabilityProviderFactory.cs

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
using System;
66
using System.Linq;
7+
using System.Reflection;
78
using Microsoft.Azure.WebJobs.Host.Scale;
8-
using Microsoft.Extensions.Configuration;
99
using Microsoft.Extensions.Logging;
1010

1111
namespace Microsoft.Azure.WebJobs.Extensions.DurableTask.FunctionsScale.AzureStorage
@@ -19,28 +19,20 @@ public class AzureStorageScalabilityProviderFactory : IScalabilityProviderFactor
1919
private const string LoggerName = "Triggers.DurableTask.AzureStorage";
2020

2121
private readonly IStorageServiceClientProviderFactory clientProviderFactory;
22-
private readonly IConfiguration configuration;
23-
private readonly INameResolver nameResolver;
2422
private readonly ILoggerFactory loggerFactory;
2523
private readonly ILogger logger;
2624

2725
/// <summary>
2826
/// Initializes a new instance of the <see cref="AzureStorageScalabilityProviderFactory"/> class.
2927
/// </summary>
3028
/// <param name="clientProviderFactory">The storage client provider factory.</param>
31-
/// <param name="configuration">The configuration for reading connection strings.</param>
32-
/// <param name="nameResolver">The name resolver for connection strings.</param>
3329
/// <param name="loggerFactory">The logger factory.</param>
3430
/// <exception cref="ArgumentNullException">Thrown when required parameters are null.</exception>
3531
public AzureStorageScalabilityProviderFactory(
3632
IStorageServiceClientProviderFactory clientProviderFactory,
37-
IConfiguration configuration,
38-
INameResolver nameResolver,
3933
ILoggerFactory loggerFactory)
4034
{
4135
this.clientProviderFactory = clientProviderFactory ?? throw new ArgumentNullException(nameof(clientProviderFactory));
42-
this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
43-
this.nameResolver = nameResolver ?? throw new ArgumentNullException(nameof(nameResolver));
4436
this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
4537
this.logger = this.loggerFactory.CreateLogger(LoggerName);
4638

@@ -59,13 +51,13 @@ public AzureStorageScalabilityProviderFactory(
5951
public string DefaultConnectionName { get; }
6052

6153
/// <summary>
62-
/// Creates and caches a default <see cref="ScalabilityProvider"/> instance using Azure Storage as the backend using
63-
/// the provided pre-deserialized metadata and trigger metadata for accessing Properties.
54+
/// Creates and caches a default <see cref="ScalabilityProvider"/> instance using Azure Storage as the backend,
55+
/// using the provided pre-deserialized metadata and trigger metadata for accessing properties.
6456
/// </summary>
6557
/// <param name="metadata">The pre-deserialized Durable Task metadata.</param>
66-
/// <param name="triggerMetadata">Trigger metadata used to access Properties like token credentials.</param>
58+
/// <param name="triggerMetadata">Trigger metadata used to access properties like token credentials.</param>
6759
/// <returns>
68-
/// A singleton instance of <see cref="AzureStorageScalabilityProvider"/>.
60+
/// An singleton instance of <see cref="AzureStorageScalabilityProvider"/>.
6961
/// </returns>
7062
public ScalabilityProvider GetScalabilityProvider(DurableTaskMetadata metadata, TriggerMetadata? triggerMetadata)
7163
{
@@ -114,23 +106,34 @@ public ScalabilityProvider GetScalabilityProvider(DurableTaskMetadata metadata,
114106
// Call it using reflection to get the TokenCredential
115107
var factoryType = componentFactoryObj.GetType();
116108
var method = factoryType.GetMethod("CreateTokenCredential");
117-
if (method != null)
109+
110+
if (method == null)
118111
{
119-
try
120-
{
121-
// Call CreateTokenCredential(null) to get the TokenCredential from the wrapper
122-
var credential = method.Invoke(componentFactoryObj, new object?[] { null });
123-
if (credential is global::Azure.Core.TokenCredential tokenCredential)
124-
{
125-
return tokenCredential;
126-
}
127-
}
128-
catch (Exception ex)
112+
logger?.LogWarning(
113+
"CreateTokenCredential method not found on the AzureComponentFactory instance provided by the scale controller. " +
114+
"Identity-based authentication cannot be used in this case.");
115+
return null;
116+
}
117+
118+
try
119+
{
120+
// Call CreateTokenCredential(null) to get the TokenCredential from the wrapper
121+
var credential = method.Invoke(componentFactoryObj, new object?[] { null });
122+
if (credential is global::Azure.Core.TokenCredential tokenCredential)
129123
{
130-
logger?.LogWarning(ex, "Failed to extract TokenCredential from AzureComponentFactory. Using null credential instead.");
131-
return null;
124+
return tokenCredential;
132125
}
133126
}
127+
catch (TargetInvocationException ex)
128+
{
129+
logger?.LogWarning(ex, "Failed to extract TokenCredential from AzureComponentFactory. Using null credential instead.");
130+
return null;
131+
}
132+
catch (TargetException ex)
133+
{
134+
logger?.LogWarning(ex, "Failed to extract TokenCredential from AzureComponentFactory. Using null credential instead.");
135+
return null;
136+
}
134137
}
135138

136139
return null;

src/Microsoft.Azure.WebJobs.Extensions.DurableTask.FunctionsScale/AzureStorage/DurableTaskTargetScaler.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ namespace Microsoft.Azure.WebJobs.Extensions.DurableTask.FunctionsScale.AzureSto
1919
public class DurableTaskTargetScaler : ITargetScaler
2020
{
2121
private readonly DurableTaskMetricsProvider metricsProvider;
22-
private readonly TargetScalerResult scaleResult;
2322
private readonly ScalabilityProvider scalabilityProvider;
2423
private readonly ILogger logger;
2524
private readonly string scaler;
@@ -47,7 +46,6 @@ public DurableTaskTargetScaler(
4746
{
4847
this.scaler = scalerId;
4948
this.metricsProvider = metricsProvider;
50-
this.scaleResult = new TargetScalerResult();
5149
this.TargetScalerDescriptor = new TargetScalerDescriptor(this.scaler);
5250
this.scalabilityProvider = scalabilityProvider;
5351
this.logger = logger;
@@ -103,7 +101,6 @@ public async Task<TargetScalerResult> GetScaleResultAsync(TargetScalerContext co
103101
var orchestratorWorkers = Math.Min(activeControlQueues, upperBoundControlWorkers);
104102

105103
int numWorkersToRequest = (int)Math.Max(activityWorkers, orchestratorWorkers);
106-
this.scaleResult.TargetWorkerCount = numWorkersToRequest;
107104

108105
// When running on ScaleController V3, ILogger logs are forwarded to the ScaleController's Kusto table.
109106
// This works because this code does not execute in the Functions Host process, but in the ScaleController process,
@@ -120,7 +117,7 @@ public async Task<TargetScalerResult> GetScaleResultAsync(TargetScalerContext co
120117
}
121118

122119
this.logger.LogInformation(scaleControllerLog);
123-
return this.scaleResult;
120+
return new TargetScalerResult { TargetWorkerCount = numWorkersToRequest };
124121
}
125122
catch (Exception ex)
126123
{

src/Microsoft.Azure.WebJobs.Extensions.DurableTask.FunctionsScale/DurableTaskMetadata.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ namespace Microsoft.Azure.WebJobs.Extensions.DurableTask.FunctionsScale
1313
/// </summary>
1414
public class DurableTaskMetadata
1515
{
16+
private const string DefaultConnectionName = "connectionName";
17+
private const string ConnectionNameOverride = "connectionStringName";
18+
1619
/// <summary>
1720
/// Gets or sets the name of the Durable Task Hub. This identifies the taskhub being monitored or scaled.
1821
/// </summary>
@@ -47,11 +50,21 @@ public class DurableTaskMetadata
4750
/// <param name="nameResolver">The name resolver used to resolve app setting placeholders.</param>
4851
public static void ResolveAppSettingOptions(DurableTaskMetadata metadata, INameResolver nameResolver)
4952
{
50-
if (metadata.StorageProvider != null &&
51-
metadata.StorageProvider.TryGetValue("connectionName", out object? connectionNameObj) &&
52-
connectionNameObj is string connectionName)
53+
if (metadata.StorageProvider == null)
54+
{
55+
return;
56+
}
57+
58+
// Resolve both "connectionName" and "connectionStringName" keys for compatibility.
59+
ResolveStorageProviderSetting(metadata.StorageProvider, DefaultConnectionName, nameResolver);
60+
ResolveStorageProviderSetting(metadata.StorageProvider, ConnectionNameOverride, nameResolver);
61+
}
62+
63+
private static void ResolveStorageProviderSetting(IDictionary<string, object> storageProvider, string key, INameResolver nameResolver)
64+
{
65+
if (storageProvider.TryGetValue(key, out object? value) && value is string name && !string.IsNullOrWhiteSpace(name))
5366
{
54-
metadata.StorageProvider["connectionName"] = nameResolver.Resolve(connectionName) ?? string.Empty;
67+
storageProvider[key] = nameResolver.Resolve(name) ?? string.Empty;
5568
}
5669
}
5770
}

src/Microsoft.Azure.WebJobs.Extensions.DurableTask.FunctionsScale/DurableTaskScaleConfigurationExtensions.cs

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,23 +39,9 @@ public static IWebJobsBuilder AddDurableTask(this IWebJobsBuilder builder)
3939

4040
IServiceCollection serviceCollection = builder.Services;
4141

42-
// Register StorageServiceClientProviderFactory using factory function to ensure proper construction
43-
serviceCollection.TryAddSingleton<IStorageServiceClientProviderFactory>(serviceProvider =>
44-
{
45-
return new StorageServiceClientProviderFactory(
46-
serviceProvider.GetRequiredService<IConfiguration>(),
47-
serviceProvider.GetRequiredService<ILoggerFactory>());
48-
});
42+
serviceCollection.TryAddSingleton<IStorageServiceClientProviderFactory, StorageServiceClientProviderFactory>();
4943

50-
// Register all scalability provider factories
51-
serviceCollection.AddSingleton<IScalabilityProviderFactory>(serviceProvider =>
52-
{
53-
return new AzureStorageScalabilityProviderFactory(
54-
serviceProvider.GetRequiredService<IStorageServiceClientProviderFactory>(),
55-
serviceProvider.GetRequiredService<IConfiguration>(),
56-
serviceProvider.GetRequiredService<INameResolver>(),
57-
serviceProvider.GetRequiredService<ILoggerFactory>());
58-
});
44+
serviceCollection.AddSingleton<IScalabilityProviderFactory, AzureStorageScalabilityProviderFactory>();
5945

6046
return builder;
6147
}
@@ -119,7 +105,7 @@ internal static IWebJobsBuilder AddDurableScaleForTrigger(this IWebJobsBuilder b
119105
}
120106
});
121107

122-
// builder.Services.AddSingleton<IScaleMonitorProvider>(serviceProvider => serviceProvider.GetServices<DurableTaskTriggersScaleProvider>().Single(x => x == provider));
108+
builder.Services.AddSingleton<IScaleMonitorProvider>(serviceProvider => serviceProvider.GetServices<DurableTaskTriggersScaleProvider>().Single(x => x == provider));
123109
builder.Services.AddSingleton<ITargetScalerProvider>(serviceProvider =>
124110
{
125111
// Get the DurableTaskTriggersScaleProvider instance - it should have been created by now

src/Microsoft.Azure.WebJobs.Extensions.DurableTask.FunctionsScale/DurableTaskTriggersScaleProvider.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ namespace Microsoft.Azure.WebJobs.Extensions.DurableTask.FunctionsScale
1515
/// </summary>
1616
public class DurableTaskTriggersScaleProvider : IScaleMonitorProvider, ITargetScalerProvider
1717
{
18-
private const string DefaultConnectionName = "connectionName";
19-
private const string ConnectionNameOverride = "connectionStringName";
20-
2118
private readonly IScaleMonitor monitor;
2219
private readonly ITargetScaler targetScaler;
2320

@@ -59,7 +56,6 @@ public DurableTaskTriggersScaleProvider(
5956
throw new InvalidOperationException($"Expected `taskHubName` property in SyncTriggers payload but found none.");
6057
}
6158

62-
// Resolve app settings (e.g., %MyConnectionString% -> actual value)
6359
DurableTaskMetadata.ResolveAppSettingOptions(metadata, nameResolver);
6460

6561
var logger = loggerFactory.CreateLogger<DurableTaskTriggersScaleProvider>();

src/Microsoft.Azure.WebJobs.Extensions.DurableTask.FunctionsScale/StorageServiceClientProviderFactory.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ namespace Microsoft.Azure.WebJobs.Extensions.DurableTask.FunctionsScale
1616
public class StorageServiceClientProviderFactory : IStorageServiceClientProviderFactory
1717
{
1818
private readonly IConfiguration configuration;
19-
private readonly ILoggerFactory loggerFactory;
2019
private readonly ILogger logger;
2120

2221
/// <summary>
@@ -33,7 +32,6 @@ public StorageServiceClientProviderFactory(
3332
ILoggerFactory loggerFactory)
3433
{
3534
this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
36-
this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
3735
this.logger = loggerFactory.CreateLogger<StorageServiceClientProviderFactory>();
3836
}
3937

src/Microsoft.Azure.WebJobs.Extensions.DurableTask.FunctionsScale/TriggerMetadataExtensions.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ namespace Microsoft.Azure.WebJobs.Extensions.DurableTask.FunctionsScale
1212
/// </summary>
1313
internal static class TriggerMetadataExtensions
1414
{
15+
private const string DefaultConnectionName = "connectionName";
16+
private const string ConnectionNameOverride = "connectionStringName";
17+
1518
/// <summary>
1619
/// For testing. Extracts DurableTaskMetadata from trigger metadata sent by the Scale Controller.
1720
/// </summary>
@@ -40,12 +43,12 @@ internal static class TriggerMetadataExtensions
4043
return null;
4144
}
4245

43-
if (storageProvider.TryGetValue("connectionName", out object? v1) && v1 is string s1 && !string.IsNullOrWhiteSpace(s1))
46+
if (storageProvider.TryGetValue(DefaultConnectionName, out object? v1) && v1 is string s1 && !string.IsNullOrWhiteSpace(s1))
4447
{
4548
return s1;
4649
}
4750

48-
if (storageProvider.TryGetValue("connectionStringName", out object? v2) && v2 is string s2 && !string.IsNullOrWhiteSpace(s2))
51+
if (storageProvider.TryGetValue(ConnectionNameOverride, out object? v2) && v2 is string s2 && !string.IsNullOrWhiteSpace(s2))
4952
{
5053
return s2;
5154
}

test/ScaleTests/AzureStorage/AzureStorageScalabilityProviderFactoryTests.cs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,6 @@ public void Constructor_ValidParameters_CreatesInstance()
5454
// Act
5555
var factory = new AzureStorageScalabilityProviderFactory(
5656
this.clientProviderFactory,
57-
this.configuration,
58-
this.nameResolver,
5957
this.loggerFactory);
6058

6159
// Assert
@@ -77,8 +75,6 @@ public void Constructor_NullClientProviderFactory_ThrowsArgumentNullException()
7775
Assert.Throws<ArgumentNullException>(() =>
7876
new AzureStorageScalabilityProviderFactory(
7977
null,
80-
this.configuration,
81-
this.nameResolver,
8278
this.loggerFactory));
8379
}
8480

@@ -96,8 +92,6 @@ public void GetScalabilityProvider_WithTriggerMetadata_ReturnsValidProvider()
9692
// Options no longer used - removed CreateOptions call
9793
var factory = new AzureStorageScalabilityProviderFactory(
9894
this.clientProviderFactory,
99-
this.configuration,
100-
this.nameResolver,
10195
this.loggerFactory);
10296

10397
var triggerMetadata = CreateTriggerMetadata("testHub", 15, 25, "TestConnection");
@@ -128,8 +122,6 @@ public void ValidateAzureStorageOptions_InvalidMaxConcurrent_ThrowsInvalidOperat
128122
// Arrange - MaxConcurrentOrchestratorFunctions is 0 (invalid)
129123
var factory = new AzureStorageScalabilityProviderFactory(
130124
this.clientProviderFactory,
131-
this.configuration,
132-
this.nameResolver,
133125
this.loggerFactory);
134126

135127
var triggerMetadata = CreateTriggerMetadata("testHub", 0, 20, "TestConnection"); // 0 is invalid
@@ -164,8 +156,6 @@ public void GetScalabilityProvider_WithMultipleConnections_CreatesProvidersSucce
164156
// Options no longer used - removed CreateOptions call
165157
var factory = new AzureStorageScalabilityProviderFactory(
166158
clientFactory,
167-
config,
168-
this.nameResolver,
169159
this.loggerFactory);
170160

171161
// Pass connection name via trigger metadata (Scale Controller behavior)

0 commit comments

Comments
 (0)