From c2e1f6a0b61b73853a81e03b8b53827afc693b97 Mon Sep 17 00:00:00 2001 From: Reuben Bond Date: Thu, 11 Jun 2026 16:18:35 -0700 Subject: [PATCH] fix: respect placement hints in local-first directors Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Placement/PreferLocalPlacementDirector.cs | 10 +++- .../Placement/StatelessWorkerDirector.cs | 6 ++ .../Runtime/LocalPlacementDirectorTests.cs | 58 +++++++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 test/Orleans.Core.Tests/Runtime/LocalPlacementDirectorTests.cs diff --git a/src/Orleans.Runtime/Placement/PreferLocalPlacementDirector.cs b/src/Orleans.Runtime/Placement/PreferLocalPlacementDirector.cs index bef3bce0636..1364b8b5966 100644 --- a/src/Orleans.Runtime/Placement/PreferLocalPlacementDirector.cs +++ b/src/Orleans.Runtime/Placement/PreferLocalPlacementDirector.cs @@ -19,8 +19,16 @@ internal class PreferLocalPlacementDirector : RandomPlacementDirector, IPlacemen public override Task 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); } diff --git a/src/Orleans.Runtime/Placement/StatelessWorkerDirector.cs b/src/Orleans.Runtime/Placement/StatelessWorkerDirector.cs index 6f358973a45..36323a40c71 100644 --- a/src/Orleans.Runtime/Placement/StatelessWorkerDirector.cs +++ b/src/Orleans.Runtime/Placement/StatelessWorkerDirector.cs @@ -9,6 +9,12 @@ public Task 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()) { diff --git a/test/Orleans.Core.Tests/Runtime/LocalPlacementDirectorTests.cs b/test/Orleans.Core.Tests/Runtime/LocalPlacementDirectorTests.cs new file mode 100644 index 00000000000..d0933ae2921 --- /dev/null +++ b/test/Orleans.Core.Tests/Runtime/LocalPlacementDirectorTests.cs @@ -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(); + placementContext.LocalSilo.Returns(localSilo); + placementContext.LocalSiloStatus.Returns(localSiloStatus); + placementContext.GetCompatibleSilos(Arg.Any()).Returns(compatibleSilos); + return placementContext; + } + + private static PlacementTarget CreateTarget(SiloAddress placementHint) => + new( + GrainId.Create("test", "grain-1"), + new Dictionary { [IPlacementDirector.PlacementHintKey] = placementHint }, + default, + 0); + + private static SiloAddress Silo(string value) => SiloAddress.FromParsableString(value); + } +}