Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -51,6 +51,7 @@ internal class JobHostContextFactory : IJobHostContextFactory
private readonly IScaleMonitorManager _monitorManager;
private readonly IDrainModeManager _drainModeManager;
private readonly IApplicationLifetime _applicationLifetime;
private readonly ITargetScalerManager _targetScalerManager;

public JobHostContextFactory(
IDashboardLoggingSetup dashboardLoggingSetup,
Expand All @@ -73,7 +74,8 @@ public JobHostContextFactory(
IAsyncCollector<FunctionInstanceLogEntry> eventCollector,
IScaleMonitorManager monitorManager,
IDrainModeManager drainModeManager,
IApplicationLifetime applicationLifetime)
IApplicationLifetime applicationLifetime,
ITargetScalerManager targetScalerManager)
{
_dashboardLoggingSetup = dashboardLoggingSetup;
_functionExecutor = functionExecutor;
Expand All @@ -96,6 +98,7 @@ public JobHostContextFactory(
_monitorManager = monitorManager;
_drainModeManager = drainModeManager;
_applicationLifetime = applicationLifetime;
_targetScalerManager = targetScalerManager;
}

public async Task<JobHostContext> Create(JobHost host, CancellationToken shutdownToken, CancellationToken cancellationToken)
Expand Down Expand Up @@ -124,7 +127,8 @@ public async Task<JobHostContext> Create(JobHost host, CancellationToken shutdow
// they are started).
host.OnHostInitialized();
};
IListenerFactory functionsListenerFactory = new HostListenerFactory(functions.ReadAll(), _singletonManager, _activator, _nameResolver, _loggerFactory, _monitorManager, listenersCreatedCallback, _jobHostOptions.Value.AllowPartialHostStartup, _drainModeManager);
IListenerFactory functionsListenerFactory = new HostListenerFactory(functions.ReadAll(), _singletonManager, _activator, _nameResolver, _loggerFactory,
_monitorManager, _targetScalerManager, listenersCreatedCallback, _jobHostOptions.Value.AllowPartialHostStartup, _drainModeManager);

string hostId = await _hostIdProvider.GetHostIdAsync(cancellationToken);
bool dashboardLoggingEnabled = _dashboardLoggingSetup.Setup(functions, functionsListenerFactory, out IFunctionExecutor hostCallExecutor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ public static IWebJobsBuilder AddWebJobs(this IServiceCollection services, Actio
services.TryAddSingleton<IHostInstanceLogger, NullHostInstanceLogger>();
services.TryAddSingleton<IDistributedLockManager, InMemoryDistributedLockManager>();
services.TryAddSingleton<IScaleMonitorManager, ScaleMonitorManager>();
services.TryAddSingleton<ITargetScalerManager, TargetScalerManager>();

services.AddSingleton<IPrimaryHostStateProvider, PrimaryHostStateProvider>();
services.AddSingleton<IHostedService, PrimaryHostCoordinator>();
Expand Down
30 changes: 27 additions & 3 deletions src/Microsoft.Azure.WebJobs.Host/Listeners/HostListenerFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using Microsoft.Azure.WebJobs.Logging;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Microsoft.Azure.WebJobs.Host.Listeners
{
Expand All @@ -30,11 +31,11 @@ internal class HostListenerFactory : IListenerFactory
private readonly bool _allowPartialHostStartup;
private readonly Action _listenersCreatedCallback;
private readonly IScaleMonitorManager _monitorManager;
private readonly ITargetScalerManager _targetScalerManager;
private readonly IDrainModeManager _drainModeManager;


public HostListenerFactory(IEnumerable<IFunctionDefinition> functionDefinitions, SingletonManager singletonManager, IJobActivator activator,
INameResolver nameResolver, ILoggerFactory loggerFactory, IScaleMonitorManager monitorManager, Action listenersCreatedCallback, bool allowPartialHostStartup = false, IDrainModeManager drainModeManager = null)
INameResolver nameResolver, ILoggerFactory loggerFactory, IScaleMonitorManager monitorManager, ITargetScalerManager targetScalerManager, Action listenersCreatedCallback, bool allowPartialHostStartup = false, IDrainModeManager drainModeManager = null)
{
_functionDefinitions = functionDefinitions;
_singletonManager = singletonManager;
Expand All @@ -44,6 +45,7 @@ public HostListenerFactory(IEnumerable<IFunctionDefinition> functionDefinitions,
_logger = _loggerFactory?.CreateLogger(LogCategories.Startup);
_allowPartialHostStartup = allowPartialHostStartup;
_monitorManager = monitorManager;
_targetScalerManager = targetScalerManager;
_listenersCreatedCallback = listenersCreatedCallback;
_drainModeManager = drainModeManager;
}
Expand Down Expand Up @@ -71,7 +73,8 @@ public async Task<IListener> CreateAsync(CancellationToken cancellationToken)
IListener listener = await listenerFactory.CreateAsync(cancellationToken);

RegisterScaleMonitor(listener, _monitorManager);

RegisterTargetScaler(listener, _targetScalerManager);

// if the listener is a Singleton, wrap it with our SingletonListener
SingletonAttribute singletonAttribute = SingletonManager.GetListenerSingletonOrNull(listener.GetType(), functionDefinition.Descriptor);
if (singletonAttribute != null)
Expand Down Expand Up @@ -123,6 +126,27 @@ internal static void RegisterScaleMonitor(IListener listener, IScaleMonitorManag
}
}

internal static void RegisterTargetScaler(IListener listener, ITargetScalerManager targetScalerManager)
{
if (listener is ITargetScaler targetScaler)
{
targetScalerManager.Register(targetScaler);
}
else if (listener is ITargetScalerProvider targetScalerProvider)
{
var scaler = ((ITargetScalerProvider)listener).GetTargetScaler();
targetScalerManager.Register(scaler);
}
else if (listener is IEnumerable<IListener>)
{
// for composite listeners, we need to check all the inner listeners
foreach (var innerListener in ((IEnumerable<IListener>)listener))
{
RegisterTargetScaler(innerListener, targetScalerManager);
}
}
}

internal static bool IsDisabled(MethodInfo method, INameResolver nameResolver, IJobActivator activator, IConfiguration configuration)
{
// First try to resolve disabled state by setting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public interface IScaleMonitorManager
/// Should only be called after the host has been started and all
/// instances are registered.
/// </remarks>
/// <returns>The collection of monitor intances.</returns>
/// <returns>The collection of monitor instances.</returns>
IEnumerable<IScaleMonitor> GetMonitors();
}
}
27 changes: 27 additions & 0 deletions src/Microsoft.Azure.WebJobs.Host/Scale/ITargetScaler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System.Threading.Tasks;

namespace Microsoft.Azure.WebJobs.Host.Scale
{
/// <summary>
/// Interface defining an Azure Functions scaler that makes scale decisions based on current
/// event source metrics and function concurrency.
/// </summary>
public interface ITargetScaler
{
/// <summary>
/// Returns the <see cref="TargetScalerDescriptor"/> for this target scaler.
/// </summary>
TargetScalerDescriptor TargetScalerDescriptor { get; }

/// <summary>
/// Return the current scale result based on the specified context.
/// </summary>
/// <param name="context">The <see cref="TargetScalerContext"/> to use to determine
/// the scale result.</param>
/// <returns>The scale result.</returns>
Task<TargetScalerResult> GetScaleResultAsync(TargetScalerContext context);
}
}
30 changes: 30 additions & 0 deletions src/Microsoft.Azure.WebJobs.Host/Scale/ITargetScalerManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System.Collections.Generic;

namespace Microsoft.Azure.WebJobs.Host.Scale
{
/// <summary>
/// Manager for registering and accessing <see cref="ITargetScaler"/> instances for
/// a <see cref="JobHost"/> instance.
/// </summary>
public interface ITargetScalerManager
{
/// <summary>
/// Register an <see cref="ITargetScaler"/> instance.
/// </summary>
/// <param name="scaler">The target scaler instance to register.</param>
void Register(ITargetScaler scaler);

/// <summary>
/// Get all registered target scaler instances.
/// </summary>
/// <remarks>
/// Should only be called after the host has been started and all
/// instances are registered.
/// </remarks>
/// <returns>The collection of target scaler instances.</returns>
IEnumerable<ITargetScaler> GetTargetScalers();
}
}
21 changes: 21 additions & 0 deletions src/Microsoft.Azure.WebJobs.Host/Scale/ITargetScalerProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

namespace Microsoft.Azure.WebJobs.Host.Scale
{
/// <summary>
/// Provider interface for returning an <see cref="ITargetScaler"/> instance.
/// </summary>
/// <remarks>
/// Listeners can implement <see cref="ITargetScaler"/> directly, but in some
/// cases the decoupling afforded by this interface is needed.
/// </remarks>
public interface ITargetScalerProvider
{
/// <summary>
/// Gets the <see cref="ITargetScaler"/> instance.
/// </summary>
/// <returns>The <see cref="ITargetScaler"/> instance.</returns>
ITargetScaler GetTargetScaler();
}
}
20 changes: 20 additions & 0 deletions src/Microsoft.Azure.WebJobs.Host/Scale/TargetScalerContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

namespace Microsoft.Azure.WebJobs.Host.Scale
{
/// <summary>
/// Context used by <see cref="ITargetScaler.GetScaleResultAsync(TargetScalerContext)"/> to decide
/// scale result.
/// </summary>
public class TargetScalerContext
{
/// <summary>
/// The current concurrency for the target function.
/// </summary>
/// <remarks>
/// When not specified, the scaler will determine the concurrency based on configuration.
/// </remarks>
public int? InstanceConcurrency { get; set; }
}
}
21 changes: 21 additions & 0 deletions src/Microsoft.Azure.WebJobs.Host/Scale/TargetScalerDescriptor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

namespace Microsoft.Azure.WebJobs.Host.Scale
{
/// <summary>
/// Metadata descriptor for an <see cref="ITargetScaler"/>.
/// </summary>
public class TargetScalerDescriptor
{
public TargetScalerDescriptor(string functionId)
{
FunctionId = functionId;
}

/// <summary>
/// Gets the ID of the function associated with this scaler.
/// </summary>
public string FunctionId { get; }
}
}
44 changes: 44 additions & 0 deletions src/Microsoft.Azure.WebJobs.Host/Scale/TargetScalerManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System.Collections.Generic;

namespace Microsoft.Azure.WebJobs.Host.Scale
{
internal class TargetScalerManager : ITargetScalerManager
{
private readonly List<ITargetScaler> _targetScalers = new List<ITargetScaler>();
private object _syncRoot = new object();

public TargetScalerManager()
{
}

public TargetScalerManager(IEnumerable<ITargetScaler> targetScalers)
{
// add any initial target scalers coming from DI
// additional monitors can be added at runtime
// via Register
_targetScalers.AddRange(targetScalers);
}

public void Register(ITargetScaler targetScaler)
{
lock (_syncRoot)
{
if (!_targetScalers.Contains(targetScaler))
{
_targetScalers.Add(targetScaler);
}
}
}

public IEnumerable<ITargetScaler> GetTargetScalers()
{
lock (_syncRoot)
{
return _targetScalers.AsReadOnly();
}
}
}
}
16 changes: 16 additions & 0 deletions src/Microsoft.Azure.WebJobs.Host/Scale/TargetScalerResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

namespace Microsoft.Azure.WebJobs.Host.Scale
{
/// <summary>
/// Represents the scale result of a target scaler.
/// </summary>
public class TargetScalerResult
{
/// <summary>
/// Gets or sets the target worker count.
/// </summary>
public int TargetWorkerCount { get; set; }
}
}
Loading