Skip to content

Commit

Permalink
Merge branch 'dev' into netherite-pr
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastianburckhardt committed Oct 29, 2020
2 parents be33048 + f95755b commit 50eebc3
Show file tree
Hide file tree
Showing 13 changed files with 1,839 additions and 1,293 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,25 +41,37 @@ internal class DurableClient : IDurableClient,
private readonly DurableTaskExtension config;
private readonly DurableClientAttribute attribute; // for rehydrating a Client after a webhook
private readonly MessagePayloadDataConverter messageDataConverter;
private readonly DurableTaskOptions durableTaskOptions;

internal DurableClient(
DurabilityProvider serviceClient,
DurableTaskExtension config,
HttpApiHandler httpHandler,
DurableClientAttribute attribute)
DurableClientAttribute attribute,
MessagePayloadDataConverter messageDataConverter,
EndToEndTraceHelper traceHelper,
DurableTaskOptions durableTaskOptions)
{
this.config = config ?? throw new ArgumentNullException(nameof(config));

this.messageDataConverter = config.MessageDataConverter;
this.messageDataConverter = messageDataConverter;

this.client = new TaskHubClient(serviceClient, this.messageDataConverter);
this.durabilityProvider = serviceClient;
this.traceHelper = config.TraceHelper;
this.traceHelper = traceHelper;
this.httpApiHandler = httpHandler;
this.hubName = attribute.TaskHub ?? config.Options.HubName;
this.durableTaskOptions = durableTaskOptions;
this.hubName = attribute.TaskHub ?? this.durableTaskOptions.HubName;
this.attribute = attribute;
}

internal DurableClient(
DurabilityProvider serviceClient,
DurableTaskExtension config,
HttpApiHandler httpHandler,
DurableClientAttribute attribute)
: this(serviceClient, httpHandler, attribute, config.MessageDataConverter, config.TraceHelper, config.Options)
{
this.config = config;
}

public string TaskHubName => this.hubName;

internal DurabilityProvider DurabilityProvider => this.durabilityProvider;
Expand Down Expand Up @@ -119,7 +131,7 @@ async Task<IActionResult> IDurableOrchestrationClient.WaitForCompletionOrCreateC
/// <inheritdoc />
async Task<string> IDurableOrchestrationClient.StartNewAsync<T>(string orchestratorFunctionName, string instanceId, T input)
{
if (this.ClientReferencesCurrentApp(this))
if (!this.attribute.ExternalClient && this.ClientReferencesCurrentApp(this))
{
this.config.ThrowIfFunctionDoesNotExist(orchestratorFunctionName, FunctionType.Orchestrator);
}
Expand Down Expand Up @@ -160,7 +172,7 @@ async Task<string> IDurableOrchestrationClient.StartNewAsync<T>(string orchestra

private OrchestrationStatus[] GetStatusesNotToOverride()
{
var overridableStates = this.config.Options.OverridableExistingInstanceStates;
var overridableStates = this.durableTaskOptions.OverridableExistingInstanceStates;
if (overridableStates == OverridableStates.NonRunningStates)
{
return new OrchestrationStatus[]
Expand Down Expand Up @@ -328,7 +340,7 @@ private bool ClientReferencesCurrentApp(DurableClient client)

private bool TaskHubMatchesCurrentApp(DurableClient client)
{
var taskHubName = this.config.Options.HubName;
var taskHubName = this.durableTaskOptions.HubName;
return client.TaskHubName.Equals(taskHubName);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System;
using System.Collections.Concurrent;
using Microsoft.Azure.WebJobs.Extensions.DurableTask.Options;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;

namespace Microsoft.Azure.WebJobs.Extensions.DurableTask.ContextImplementations
{
/// <summary>
/// Factory class to create Durable Client to start works outside an azure function context.
/// </summary>
public class DurableClientFactory : IDurableClientFactory, IDisposable
{
// Creating client objects is expensive, so we cache them when the attributes match.
// Note that DurableClientAttribute defines a custom equality comparer.
private readonly ConcurrentDictionary<DurableClientAttribute, DurableClient> cachedClients =
new ConcurrentDictionary<DurableClientAttribute, DurableClient>();

private readonly ConcurrentDictionary<DurableClientAttribute, HttpApiHandler> cachedHttpListeners =
new ConcurrentDictionary<DurableClientAttribute, HttpApiHandler>();

private readonly DurableClientOptions defaultDurableClientOptions;
private readonly DurableTaskOptions durableTaskOptions;
private readonly IDurabilityProviderFactory durabilityProviderFactory;
private readonly ILogger logger;

/// <summary>
/// Initializes a new instance of the <see cref="DurableClientFactory"/> class.
/// </summary>
/// <param name="defaultDurableClientOptions">Default Options to Build Durable Clients.</param>
/// <param name="orchestrationServiceFactory">The factory used to create orchestration service based on the configured storage provider.</param>
/// <param name="loggerFactory">The logger factory used for extension-specific logging and orchestration tracking.</param>
/// <param name="durableTaskOptions">The configuration options for this extension.</param>
/// <param name="messageSerializerSettingsFactory">The factory used to create <see cref="JsonSerializerSettings"/> for message settings.</param>
public DurableClientFactory(
IOptions<DurableClientOptions> defaultDurableClientOptions,
IOptions<DurableTaskOptions> durableTaskOptions,
IDurabilityProviderFactory orchestrationServiceFactory,
ILoggerFactory loggerFactory,
IMessageSerializerSettingsFactory messageSerializerSettingsFactory = null)
{
this.logger = loggerFactory.CreateLogger(DurableTaskExtension.LoggerCategoryName);

this.durabilityProviderFactory = orchestrationServiceFactory;
this.defaultDurableClientOptions = defaultDurableClientOptions.Value;
this.durableTaskOptions = durableTaskOptions?.Value ?? new DurableTaskOptions();

this.MessageDataConverter = DurableTaskExtension.CreateMessageDataConverter(messageSerializerSettingsFactory);
this.TraceHelper = new EndToEndTraceHelper(this.logger, this.durableTaskOptions.Tracing.TraceReplayEvents);
}

internal MessagePayloadDataConverter MessageDataConverter { get; private set; }

internal EndToEndTraceHelper TraceHelper { get; private set; }

/// <summary>
/// Gets a <see cref="IDurableClient"/> using configuration from a <see cref="DurableClientOptions"/> instance.
/// </summary>
/// <param name="durableClientOptions">options containing the client configuration parameters.</param>
/// <returns>Returns a <see cref="IDurableClient"/> instance. The returned instance may be a cached instance.</returns>
public IDurableClient CreateClient(DurableClientOptions durableClientOptions)
{
if (durableClientOptions == null)
{
throw new ArgumentException("Please configure 'DurableClientOptions'");
}

if (string.IsNullOrWhiteSpace(durableClientOptions.TaskHub))
{
throw new ArgumentException("Please provide value for 'TaskHub'");
}

DurableClientAttribute attribute = new DurableClientAttribute(durableClientOptions);

HttpApiHandler httpApiHandler = this.cachedHttpListeners.GetOrAdd(
attribute,
attr =>
{
return new HttpApiHandler(null, null, this.durableTaskOptions, this.logger);
});

DurableClient client = this.cachedClients.GetOrAdd(
attribute,
attr =>
{
DurabilityProvider innerClient = this.durabilityProviderFactory.GetDurabilityProvider(attribute);
return new DurableClient(innerClient, httpApiHandler, attribute, this.MessageDataConverter, this.TraceHelper, this.durableTaskOptions);
});

return client;
}

/// <summary>
/// Gets a <see cref="IDurableClient"/> using configuration from a <see cref="DurableClientOptions"/> instance.
/// </summary>
/// <returns>Returns a <see cref="IDurableClient"/> instance. The returned instance may be a cached instance.</returns>
public IDurableClient CreateClient()
{
return this.CreateClient(this.defaultDurableClientOptions);
}

/// <inheritdoc />
public void Dispose()
{
foreach (var cachedHttpListener in this.cachedHttpListeners)
{
cachedHttpListener.Value?.Dispose();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using Microsoft.Azure.WebJobs.Extensions.DurableTask.Options;

namespace Microsoft.Azure.WebJobs.Extensions.DurableTask.ContextImplementations
{
/// <summary>
/// Factory class to create Durable Client to start works outside an azure function context.
/// </summary>
public interface IDurableClientFactory
{
/// <summary>
/// Gets a <see cref="IDurableClient"/> using configuration from a <see cref="DurableClientOptions"/> instance.
/// </summary>
/// <param name="durableClientOptions">options containing the client configuration parameters.</param>
/// <returns>Returns a <see cref="IDurableClient"/> instance. The returned instance may be a cached instance.</returns>
IDurableClient CreateClient(DurableClientOptions durableClientOptions);

/// <summary>
/// Gets a <see cref="IDurableClient"/> using configuration from a <see cref="DurableClientOptions"/> instance.
/// </summary>
/// <returns>Returns a <see cref="IDurableClient"/> instance. The returned instance may be a cached instance.</returns>
IDurableClient CreateClient();
}
}
22 changes: 22 additions & 0 deletions src/WebJobs.Extensions.DurableTask/DurableClientAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Diagnostics;
using Microsoft.Azure.WebJobs.Description;
using Microsoft.Azure.WebJobs.Extensions.DurableTask.Options;

namespace Microsoft.Azure.WebJobs.Extensions.DurableTask
{
Expand All @@ -15,6 +16,22 @@ namespace Microsoft.Azure.WebJobs.Extensions.DurableTask
[Binding]
public class DurableClientAttribute : Attribute, IEquatable<DurableClientAttribute>
{
/// <summary>
/// Initializes a new instance of the <see cref="DurableClientAttribute"/> class.
/// </summary>
public DurableClientAttribute() { }

/// <summary>
/// Initializes a new instance of the <see cref="DurableClientAttribute"/> class.
/// </summary>
/// <param name="durableClientOptions">durable client options</param>
public DurableClientAttribute(DurableClientOptions durableClientOptions)
{
this.TaskHub = durableClientOptions.TaskHub;
this.ConnectionName = durableClientOptions.ConnectionName;
this.ExternalClient = durableClientOptions.IsExternalClient;
}

/// <summary>
/// Optional. Gets or sets the name of the task hub in which the orchestration data lives.
/// </summary>
Expand All @@ -39,6 +56,11 @@ public class DurableClientAttribute : Attribute, IEquatable<DurableClientAttribu
/// </remarks>
public string ConnectionName { get; set; }

/// <summary>
/// Indicate if the client is External from the azure function where orchestrator functions are hosted.
/// </summary>
public bool ExternalClient { get; set; }

/// <summary>
/// Returns a hash code for this attribute.
/// </summary>
Expand Down
Loading

0 comments on commit 50eebc3

Please sign in to comment.