Skip to content

Commit 56e9c45

Browse files
authored
Adding Storage Backend Selection in host.json (#1702)
These changes allow customers to configure which storage backend they want their app to use (e.g. Azure Storage, MicrosoftSQL, or Netherite) with a new type attribute under durableTask/storageProvider in host.json. The extension will register all storage backend services and then choose which one to use in the DurableTaskExtension constructor. Adding the ability to register all backends and then choose a storage backend allows us to deploy all storage backend packages through extension bundles. The main changes in this PR are allowing the extension to register a collection of services through dependency injection, adding a Name property in IDurabilityProviderFactory, and choosing the DurabilityProvider by comparing a customers configured type with the Name property in the available IDurabilityProviderFactory implementations. Resolves #1666
1 parent a7d8dfd commit 56e9c45

17 files changed

+325
-35
lines changed

release_notes.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
1-
Improved concurrency defaults for the App Service Consumption plan (https://github.com/Azure/azure-functions-durable-extension/pull/1706)
1+
## New Features
2+
- Added support to select a storage backend provider when multiple are installed (#1702): Select which storage backend to use by setting the `type` field under `durableTask/storageProvider` in host.json. If this field isn't set, then the storage backend will default to using Azure Storage.
3+
4+
- Improved concurrency defaults for the App Service Consumption plan (https://github.com/Azure/azure-functions-durable-extension/pull/1706)
5+

src/WebJobs.Extensions.DurableTask/AzureStorageDurabilityProviderFactory.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ namespace Microsoft.Azure.WebJobs.Extensions.DurableTask
1111
{
1212
internal class AzureStorageDurabilityProviderFactory : IDurabilityProviderFactory
1313
{
14+
internal const string ProviderName = "AzureStorage";
15+
1416
private readonly DurableTaskOptions options;
1517
private readonly AzureStorageOptions azureStorageOptions;
1618
private readonly IConnectionStringResolver connectionStringResolver;
@@ -59,6 +61,8 @@ public AzureStorageDurabilityProviderFactory(
5961
this.defaultConnectionName = this.azureStorageOptions.ConnectionStringName ?? ConnectionStringNames.Storage;
6062
}
6163

64+
public string Name => ProviderName;
65+
6266
internal string GetDefaultStorageConnectionString()
6367
{
6468
return this.connectionStringResolver.Resolve(this.defaultConnectionName);

src/WebJobs.Extensions.DurableTask/DurableTaskExtension.cs

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ public class DurableTaskExtension :
4646
INameVersionObjectManager<TaskOrchestration>,
4747
INameVersionObjectManager<TaskActivity>
4848
{
49+
private const string DefaultProvider = AzureStorageDurabilityProviderFactory.ProviderName;
50+
4951
internal static readonly string LoggerCategoryName = LogCategories.CreateTriggerCategory("DurableTask");
5052

5153
// Creating client objects is expensive, so we cache them when the attributes match.
@@ -81,7 +83,6 @@ public class DurableTaskExtension :
8183
private bool isTaskHubWorkerStarted;
8284
private HttpClient durableHttpClient;
8385
private EventSourceListener eventSourceListener;
84-
8586
#if FUNCTIONS_V1
8687
private IConnectionStringResolver connectionStringResolver;
8788

@@ -103,7 +104,7 @@ public DurableTaskExtension()
103104
/// <param name="options">The configuration options for this extension.</param>
104105
/// <param name="loggerFactory">The logger factory used for extension-specific logging and orchestration tracking.</param>
105106
/// <param name="nameResolver">The name resolver to use for looking up application settings.</param>
106-
/// <param name="orchestrationServiceFactory">The factory used to create orchestration service based on the configured storage provider.</param>
107+
/// <param name="orchestrationServiceFactories">The factories used to create orchestration service based on the configured storage provider.</param>
107108
/// <param name="durableHttpMessageHandlerFactory">The HTTP message handler that handles HTTP requests and HTTP responses.</param>
108109
/// <param name="hostLifetimeService">The host shutdown notification service for detecting and reacting to host shutdowns.</param>
109110
/// <param name="lifeCycleNotificationHelper">The lifecycle notification helper used for custom orchestration tracking.</param>
@@ -116,7 +117,7 @@ public DurableTaskExtension(
116117
IOptions<DurableTaskOptions> options,
117118
ILoggerFactory loggerFactory,
118119
INameResolver nameResolver,
119-
IDurabilityProviderFactory orchestrationServiceFactory,
120+
IEnumerable<IDurabilityProviderFactory> orchestrationServiceFactories,
120121
IApplicationLifetimeWrapper hostLifetimeService,
121122
IDurableHttpMessageHandlerFactory durableHttpMessageHandlerFactory = null,
122123
ILifeCycleNotificationHelper lifeCycleNotificationHelper = null,
@@ -145,7 +146,7 @@ public DurableTaskExtension(
145146

146147
this.TraceHelper = new EndToEndTraceHelper(logger, this.Options.Tracing.TraceReplayEvents);
147148
this.LifeCycleNotificationHelper = lifeCycleNotificationHelper ?? this.CreateLifeCycleNotificationHelper();
148-
this.durabilityProviderFactory = orchestrationServiceFactory;
149+
this.durabilityProviderFactory = this.GetDurabilityProviderFactory(this.Options, logger, orchestrationServiceFactories);
149150
this.defaultDurabilityProvider = this.durabilityProviderFactory.GetDurabilityProvider();
150151
this.isOptionsConfigured = true;
151152

@@ -178,15 +179,15 @@ internal DurableTaskExtension(
178179
IOptions<DurableTaskOptions> options,
179180
ILoggerFactory loggerFactory,
180181
INameResolver nameResolver,
181-
IDurabilityProviderFactory orchestrationServiceFactory,
182+
IEnumerable<IDurabilityProviderFactory> orchestrationServiceFactories,
182183
IConnectionStringResolver connectionStringResolver,
183184
IApplicationLifetimeWrapper shutdownNotification,
184185
IDurableHttpMessageHandlerFactory durableHttpMessageHandlerFactory,
185186
#pragma warning disable CS0612 // Type or member is obsolete
186187
IPlatformInformationService platformInformationService)
187188
#pragma warning restore CS0612 // Type or member is obsolete
188189

189-
: this(options, loggerFactory, nameResolver, orchestrationServiceFactory, shutdownNotification, durableHttpMessageHandlerFactory)
190+
: this(options, loggerFactory, nameResolver, orchestrationServiceFactories, shutdownNotification, durableHttpMessageHandlerFactory)
190191
{
191192
this.connectionStringResolver = connectionStringResolver;
192193
this.platformInformationService = platformInformationService;
@@ -249,6 +250,37 @@ private MessagePayloadDataConverter CreateErrorDataConverter(IErrorSerializerSet
249250
return new MessagePayloadDataConverter(errorSerializerSettingsFactory.CreateJsonSerializerSettings(), isDefault);
250251
}
251252

253+
private IDurabilityProviderFactory GetDurabilityProviderFactory(DurableTaskOptions options, ILogger logger, IEnumerable<IDurabilityProviderFactory> orchestrationServiceFactories)
254+
{
255+
bool storageTypeIsConfigured = options.StorageProvider.TryGetValue("type", out object storageType);
256+
257+
if (!storageTypeIsConfigured)
258+
{
259+
try
260+
{
261+
IDurabilityProviderFactory defaultFactory = orchestrationServiceFactories.First(f => f.Name.Equals(DefaultProvider));
262+
logger.LogInformation($"Using the default storage provider: {DefaultProvider}.");
263+
return defaultFactory;
264+
}
265+
catch (InvalidOperationException e)
266+
{
267+
throw new InvalidOperationException($"Couldn't find the default storage provider: {DefaultProvider}.", e);
268+
}
269+
}
270+
271+
try
272+
{
273+
IDurabilityProviderFactory selectedFactory = orchestrationServiceFactories.First(f => string.Equals(f.Name, storageType.ToString(), StringComparison.OrdinalIgnoreCase));
274+
logger.LogInformation($"Using the {storageType} storage provider.");
275+
return selectedFactory;
276+
}
277+
catch (InvalidOperationException e)
278+
{
279+
IList<string> factoryNames = orchestrationServiceFactories.Select(f => f.Name).ToList();
280+
throw new InvalidOperationException($"Storage provider type ({storageType}) was not found. Available storage providers: {string.Join(", ", factoryNames)}.", e);
281+
}
282+
}
283+
252284
internal string GetBackendInfo()
253285
{
254286
return this.defaultDurabilityProvider.GetBackendInfo();

src/WebJobs.Extensions.DurableTask/DurableTaskJobHostConfigurationExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public static IWebJobsBuilder AddDurableTask(this IWebJobsBuilder builder)
8080
.Services.AddSingleton<IConnectionStringResolver, WebJobsConnectionStringProvider>();
8181

8282
serviceCollection.TryAddSingleton<IDurableHttpMessageHandlerFactory, DurableHttpMessageHandlerFactory>();
83-
serviceCollection.TryAddSingleton<IDurabilityProviderFactory, AzureStorageDurabilityProviderFactory>();
83+
serviceCollection.AddSingleton<IDurabilityProviderFactory, AzureStorageDurabilityProviderFactory>();
8484
serviceCollection.TryAddSingleton<IMessageSerializerSettingsFactory, MessageSerializerSettingsFactory>();
8585
serviceCollection.TryAddSingleton<IErrorSerializerSettingsFactory, ErrorSerializerSettingsFactory>();
8686
serviceCollection.TryAddSingleton<IApplicationLifetimeWrapper, HostLifecycleService>();

src/WebJobs.Extensions.DurableTask/IDurabilityProviderFactory.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ namespace Microsoft.Azure.WebJobs.Extensions.DurableTask
88
/// </summary>
99
public interface IDurabilityProviderFactory
1010
{
11+
/// <summary>
12+
/// Specifies the Durability Provider Factory name.
13+
/// </summary>
14+
string Name { get; }
15+
1116
/// <summary>
1217
/// Creates or retrieves a durability provider to be used throughout the extension.
1318
/// </summary>

src/WebJobs.Extensions.DurableTask/Microsoft.Azure.WebJobs.Extensions.DurableTask-net461.xml

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/WebJobs.Extensions.DurableTask/Microsoft.Azure.WebJobs.Extensions.DurableTask.xml

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/Common/DurableClientBaseTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ private static DurableTaskExtension GetDurableTaskConfig()
337337
wrappedOptions,
338338
new LoggerFactory(),
339339
nameResolver,
340-
serviceFactory,
340+
new[] { serviceFactory },
341341
new TestHostShutdownNotificationService(),
342342
new DurableHttpMessageHandlerFactory(),
343343
platformInformationService: platformInformationService);

0 commit comments

Comments
 (0)