Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure StatelessWorkerAttribute.MaxLocal property is accounted for #8885

Merged
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 @@ -29,7 +29,7 @@ public GrainDirectoryResolver(
var services = serviceProvider.GetGrainDirectories();
foreach (var svc in services)
{
this.directoryPerName.Add(svc.Name, svc.Service);
this.directoryPerName.Add(svc.Name, serviceProvider.GetRequiredKeyedService<IGrainDirectory>(svc.Name));
}

this.directoryPerName.TryGetValue(GrainDirectoryAttribute.DEFAULT_GRAIN_DIRECTORY, out var defaultDirectory);
Expand Down
3 changes: 1 addition & 2 deletions src/Orleans.Runtime/Hosting/DefaultSiloServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,7 @@ internal static void AddDefaultServices(ISiloBuilder builder)
// Placement directors
services.AddPlacementDirector<RandomPlacement, RandomPlacementDirector>();
services.AddPlacementDirector<PreferLocalPlacement, PreferLocalPlacementDirector>();
services.AddPlacementDirector<StatelessWorkerPlacement, StatelessWorkerDirector>();
services.Replace(new ServiceDescriptor(typeof(StatelessWorkerPlacement), sp => new StatelessWorkerPlacement(), ServiceLifetime.Singleton));
services.AddPlacementDirector<StatelessWorkerPlacement, StatelessWorkerDirector>(ServiceLifetime.Transient);
services.AddPlacementDirector<ActivationCountBasedPlacement, ActivationCountPlacementDirector>();
services.AddPlacementDirector<HashBasedPlacement, HashBasedPlacementDirector>();
services.AddPlacementDirector<ClientObserversPlacement, ClientObserversPlacementDirector>();
Expand Down
45 changes: 17 additions & 28 deletions src/Orleans.Runtime/Hosting/DirectorySiloBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Orleans.GrainDirectory;
using Orleans.Hosting;
Expand All @@ -21,7 +19,7 @@ public static class DirectorySiloBuilderExtensions
/// <param name="implementationFactory">Factory to build the grain directory provider.</param>
/// <returns>The silo builder.</returns>
public static ISiloBuilder AddGrainDirectory<T>(this ISiloBuilder builder, string name, Func<IServiceProvider, string, T> implementationFactory)
where T : IGrainDirectory
where T : class, IGrainDirectory
{
builder.Services.AddGrainDirectory<T>(name, implementationFactory);
return builder;
Expand All @@ -37,38 +35,29 @@ public static ISiloBuilder AddGrainDirectory<T>(this ISiloBuilder builder, strin
/// <param name="implementationFactory">Factory to build the grain directory provider.</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddGrainDirectory<T>(this IServiceCollection collection, string name, Func<IServiceProvider, string, T> implementationFactory)
where T : IGrainDirectory
where T : class, IGrainDirectory
{
collection.AddSingleton(sp => new NamedService<IGrainDirectory>(name, implementationFactory(sp, name)));
// Check if the grain directory implements ILifecycleParticipant<ISiloLifecycle>
if (typeof(ILifecycleParticipant<ISiloLifecycle>).IsAssignableFrom(typeof(T)))
{
collection.AddSingleton(s => (ILifecycleParticipant<ISiloLifecycle>)s.GetGrainDirectory(name));
}
// Register the grain directory name so that directories can be enumerated by name.
collection.AddSingleton(sp => new NamedService<IGrainDirectory>(name));

// Register the grain directory implementation.
collection.AddKeyedSingleton<IGrainDirectory>(name, (sp, key) => implementationFactory(sp, name));
collection.AddSingleton<ILifecycleParticipant<ISiloLifecycle>>(s =>
s.GetKeyedService<IGrainDirectory>(name) as ILifecycleParticipant<ISiloLifecycle> ?? NoOpLifecycleParticipant.Instance);

return collection;
}

/// <summary>
/// Get the directory registered with <paramref name="name"/>.
/// </summary>
/// <param name="serviceProvider">The service provider.</param>
/// <param name="name">The name of the grain directory to resolve.</param>
/// <returns>The grain directory registered with <paramref name="name"/>, or <code>null</code> if it is not found</returns>
public static IGrainDirectory GetGrainDirectory(this IServiceProvider serviceProvider, string name)
internal static IEnumerable<NamedService<IGrainDirectory>> GetGrainDirectories(this IServiceProvider serviceProvider)
{
foreach (var directory in serviceProvider.GetGrainDirectories())
{
if (directory.Name.Equals(name))
{
return directory.Service;
}
}
return null;
return serviceProvider.GetServices<NamedService<IGrainDirectory>>() ?? [];
}

internal static IEnumerable<NamedService<IGrainDirectory>> GetGrainDirectories(this IServiceProvider serviceProvider)
private sealed class NoOpLifecycleParticipant : ILifecycleParticipant<ISiloLifecycle>
{
return serviceProvider.GetServices<NamedService<IGrainDirectory>>() ?? Enumerable.Empty<NamedService<IGrainDirectory>>();
public static readonly NoOpLifecycleParticipant Instance = new();

public void Participate(ISiloLifecycle observer) { }
}
}
}
16 changes: 5 additions & 11 deletions src/Orleans.Runtime/Hosting/NamedService.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
namespace Orleans.Runtime.Hosting
using System;

namespace Orleans.Runtime.Hosting
{
internal class NamedService<TService>
internal class NamedService<TService>(string name)
{
public NamedService(string name, TService service)
{
Name= name;
Service = service;
}

public string Name { get; }

public TService Service { get; }
public string Name { get; } = name;
}
}
29 changes: 25 additions & 4 deletions src/Orleans.Runtime/Hosting/PlacementStrategyExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Orleans.Runtime;
using Orleans.Runtime.Hosting;
using Orleans.Runtime.Placement;

namespace Orleans.Hosting
Expand Down Expand Up @@ -46,14 +45,25 @@ public static ISiloBuilder AddPlacementDirector<TStrategy>(this ISiloBuilder bui
/// <param name="services">The service collection.</param>
/// <returns>The service collection.</returns>
public static void AddPlacementDirector<TStrategy, TDirector>(this IServiceCollection services)
where TStrategy : PlacementStrategy, new()
where TDirector : class, IPlacementDirector => services.AddPlacementDirector<TStrategy, TDirector>(ServiceLifetime.Singleton);

/// <summary>
/// Configures a <typeparamref name="TDirector"/> as the placement director for placement strategy <typeparamref name="TStrategy"/>.
/// </summary>
/// <typeparam name="TStrategy">The placement strategy.</typeparam>
/// <typeparam name="TDirector">The placement director.</typeparam>
/// <param name="services">The service collection.</param>
/// <param name="strategyLifetime">The lifetime of the placement strategy.</param>
/// <returns>The service collection.</returns>
public static void AddPlacementDirector<TStrategy, TDirector>(this IServiceCollection services, ServiceLifetime strategyLifetime)
where TStrategy : PlacementStrategy, new()
where TDirector : class, IPlacementDirector
{
services.AddSingleton(new NamedService<PlacementStrategy>(typeof(TStrategy).Name, new TStrategy()));
services.Add(ServiceDescriptor.DescribeKeyed(typeof(PlacementStrategy), typeof(TStrategy).Name, typeof(TStrategy), strategyLifetime));
services.AddKeyedSingleton<IPlacementDirector, TDirector>(typeof(TStrategy));
}


/// <summary>
/// Adds a placement director.
/// </summary>
Expand All @@ -62,9 +72,20 @@ public static void AddPlacementDirector<TStrategy, TDirector>(this IServiceColle
/// <param name="createDirector">The delegate used to create the placement director.</param>
/// <returns>The service collection.</returns>
public static void AddPlacementDirector<TStrategy>(this IServiceCollection services, Func<IServiceProvider, IPlacementDirector> createDirector)
where TStrategy : PlacementStrategy, new() => services.AddPlacementDirector<TStrategy>(createDirector, ServiceLifetime.Singleton);

/// <summary>
/// Adds a placement director.
/// </summary>
/// <typeparam name="TStrategy">The placement strategy.</typeparam>
/// <param name="services">The service collection.</param>
/// <param name="createDirector">The delegate used to create the placement director.</param>
/// <param name="strategyLifetime">The lifetime of the placement strategy.</param>
/// <returns>The service collection.</returns>
public static void AddPlacementDirector<TStrategy>(this IServiceCollection services, Func<IServiceProvider, IPlacementDirector> createDirector, ServiceLifetime strategyLifetime)
where TStrategy : PlacementStrategy, new()
{
services.AddSingleton(new NamedService<PlacementStrategy>(typeof(TStrategy).Name, new TStrategy()));
services.Add(ServiceDescriptor.DescribeKeyed(typeof(PlacementStrategy), typeof(TStrategy).Name, typeof(TStrategy), strategyLifetime));
services.AddKeyedSingleton<IPlacementDirector>(typeof(TStrategy), (sp, type) => createDirector(sp));
}
}
Expand Down
24 changes: 8 additions & 16 deletions src/Orleans.Runtime/Placement/PlacementStrategyResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using System.Collections.Immutable;
using System.Collections.Concurrent;
using Orleans.Runtime.Hosting;
using System.Collections.Frozen;
using Orleans.GrainDirectory;

namespace Orleans.Runtime.Placement
{
Expand All @@ -18,8 +20,8 @@ public sealed class PlacementStrategyResolver
private readonly Func<GrainType, PlacementStrategy> _getStrategyInternal;
private readonly IPlacementStrategyResolver[] _resolvers;
private readonly GrainPropertiesResolver _grainPropertiesResolver;
private readonly ImmutableDictionary<string, PlacementStrategy> _strategies;
private readonly PlacementStrategy _defaultPlacementStrategy;
private readonly IServiceProvider _services;

/// <summary>
/// Create a <see cref="PlacementStrategyResolver"/> instance.
Expand All @@ -29,30 +31,19 @@ public PlacementStrategyResolver(
IEnumerable<IPlacementStrategyResolver> resolvers,
GrainPropertiesResolver grainPropertiesResolver)
{
_services = services;
_getStrategyInternal = GetPlacementStrategyInternal;
_resolvers = resolvers.ToArray();
_grainPropertiesResolver = grainPropertiesResolver;
_defaultPlacementStrategy = services.GetService<PlacementStrategy>();
_strategies = GetAllStrategies(services);

static ImmutableDictionary<string, PlacementStrategy> GetAllStrategies(IServiceProvider services)
{
var builder = ImmutableDictionary.CreateBuilder<string, PlacementStrategy>();
foreach (var service in services.GetServices<NamedService<PlacementStrategy>>())
{
builder[service.Name] = service.Service;
}

return builder.ToImmutable();
}
}

/// <summary>
/// Gets the placement strategy associated with the provided grain type.
/// </summary>
public PlacementStrategy GetPlacementStrategy(GrainType grainType) => _resolvedStrategies.GetOrAdd(grainType, _getStrategyInternal);

internal bool TryGetNonDefaultPlacementStrategy(GrainType grainType, out PlacementStrategy strategy)
private bool TryGetNonDefaultPlacementStrategy(GrainType grainType, out PlacementStrategy strategy)
{
_grainPropertiesResolver.TryGetGrainProperties(grainType, out var properties);

Expand All @@ -68,14 +59,15 @@ internal bool TryGetNonDefaultPlacementStrategy(GrainType grainType, out Placeme
&& properties.Properties.TryGetValue(WellKnownGrainTypeProperties.PlacementStrategy, out var placementStrategyId)
&& !string.IsNullOrWhiteSpace(placementStrategyId))
{
if (_strategies.TryGetValue(placementStrategyId, out strategy))
strategy = _services.GetKeyedService<PlacementStrategy>(placementStrategyId);
if (strategy is not null)
{
strategy.Initialize(properties);
return true;
}
else
{
throw new KeyNotFoundException($"Could not resolve placement strategy {placementStrategyId} for grain type {grainType}");
throw new KeyNotFoundException($"Could not resolve placement strategy {placementStrategyId} for grain type {grainType}.");
}
}

Expand Down
12 changes: 9 additions & 3 deletions test/Grains/TestGrainInterfaces/IPlacementTestGrain.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Net;
using System.Net;
using Orleans.Runtime;

namespace UnitTests.GrainInterfaces
Expand Down Expand Up @@ -29,8 +29,14 @@ public interface IRandomPlacementTestGrain : IPlacementTestGrain
public interface IPreferLocalPlacementTestGrain : IPlacementTestGrain
{ }

public interface ILocalPlacementTestGrain : IPlacementTestGrain
{ }
public interface IStatelessWorkerPlacementTestGrain : IPlacementTestGrain
{
ValueTask<int> GetWorkerLimit();
}

public interface IOtherStatelessWorkerPlacementTestGrain : IStatelessWorkerPlacementTestGrain
{
}

internal interface IDefaultPlacementTestGrain
{
Expand Down
Loading
Loading