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
10 changes: 9 additions & 1 deletion src/Orleans.Runtime/Placement/PreferLocalPlacementDirector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,16 @@ internal class PreferLocalPlacementDirector : RandomPlacementDirector, IPlacemen
public override Task<SiloAddress>
OnAddActivation(PlacementStrategy strategy, PlacementTarget target, IPlacementContext context)
{
var compatibleSilos = context.GetCompatibleSilos(target);

// If a valid placement hint was specified, use it.
if (IPlacementDirector.GetPlacementHint(target.RequestContextData, compatibleSilos) is { } placementHint)
{
return Task.FromResult(placementHint);
}

// if local silo is not active or does not support this type of grain, revert to random placement
if (context.LocalSiloStatus != SiloStatus.Active || !context.GetCompatibleSilos(target).Contains(context.LocalSilo))
if (context.LocalSiloStatus != SiloStatus.Active || !compatibleSilos.Contains(context.LocalSilo))
{
return base.OnAddActivation(strategy, target, context);
}
Expand Down
6 changes: 6 additions & 0 deletions src/Orleans.Runtime/Placement/StatelessWorkerDirector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ public Task<SiloAddress> OnAddActivation(PlacementStrategy strategy, PlacementTa
{
var compatibleSilos = context.GetCompatibleSilos(target);

// If a valid placement hint was specified, use it.
if (IPlacementDirector.GetPlacementHint(target.RequestContextData, compatibleSilos) is { } placementHint)
{
return Task.FromResult(placementHint);
}

// If the current silo is not shutting down, place locally if we are compatible
if (!context.LocalSiloStatus.IsTerminating())
{
Expand Down
58 changes: 58 additions & 0 deletions test/Orleans.Core.Tests/Runtime/LocalPlacementDirectorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System.Collections.Generic;
using NSubstitute;
using Orleans.Runtime;
using Orleans.Runtime.Placement;
using Xunit;

namespace UnitTests.Runtime
{
[TestCategory("BVT"), TestCategory("Placement")]
public class LocalPlacementDirectorTests
{
[Fact]
public async Task PreferLocalPlacementDirector_UsesPlacementHintWhenLocalSiloIsCompatible()
{
var localSilo = Silo("127.0.0.1:100@1");
var hintedSilo = Silo("127.0.0.1:101@1");
var director = new PreferLocalPlacementDirector();
var placementContext = CreatePlacementContext(localSilo, SiloStatus.Active, localSilo, hintedSilo);
var target = CreateTarget(hintedSilo);

var result = await director.OnAddActivation(strategy: null!, target, placementContext);

Assert.Equal(hintedSilo, result);
}

[Fact]
public async Task StatelessWorkerDirector_UsesPlacementHintWhenLocalSiloIsCompatible()
{
var localSilo = Silo("127.0.0.1:100@1");
var hintedSilo = Silo("127.0.0.1:101@1");
var director = new StatelessWorkerDirector();
var placementContext = CreatePlacementContext(localSilo, SiloStatus.Active, localSilo, hintedSilo);
var target = CreateTarget(hintedSilo);

var result = await director.OnAddActivation(strategy: null!, target, placementContext);

Assert.Equal(hintedSilo, result);
}

private static IPlacementContext CreatePlacementContext(SiloAddress localSilo, SiloStatus localSiloStatus, params SiloAddress[] compatibleSilos)
{
var placementContext = Substitute.For<IPlacementContext>();
placementContext.LocalSilo.Returns(localSilo);
placementContext.LocalSiloStatus.Returns(localSiloStatus);
placementContext.GetCompatibleSilos(Arg.Any<PlacementTarget>()).Returns(compatibleSilos);
return placementContext;
}

private static PlacementTarget CreateTarget(SiloAddress placementHint) =>
new(
GrainId.Create("test", "grain-1"),
new Dictionary<string, object> { [IPlacementDirector.PlacementHintKey] = placementHint },
default,
0);

private static SiloAddress Silo(string value) => SiloAddress.FromParsableString(value);
}
}
Loading