Skip to content

Commit

Permalink
Fix #8873 StatelessWorker MaxLocal property not being correctly accou…
Browse files Browse the repository at this point in the history
…nted for
  • Loading branch information
ReubenBond committed Feb 27, 2024
1 parent eff88b9 commit e0d75e0
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 279 deletions.
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 @@ -200,8 +200,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

0 comments on commit e0d75e0

Please sign in to comment.