Skip to content

Commit 167933c

Browse files
Fully Adaptive Stateless Worker (#9378)
Co-authored-by: Reuben Bond <[email protected]> Co-authored-by: Reuben Bond <[email protected]>
1 parent 6b17f7b commit 167933c

File tree

11 files changed

+656
-280
lines changed

11 files changed

+656
-280
lines changed

src/Orleans.Core.Abstractions/Concurrency/GrainAttributeConcurrency.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Orleans.Runtime;
55
using System;
66
using System.Collections.Generic;
7+
using System.Globalization;
78

89
namespace Orleans.Concurrency
910
{
@@ -57,14 +58,21 @@ public sealed class StatelessWorkerAttribute : PlacementAttribute, IGrainPropert
5758
/// <summary>
5859
/// Initializes a new instance of the <see cref="StatelessWorkerAttribute"/> class.
5960
/// </summary>
60-
/// <param name="maxLocalWorkers">
61-
/// The maximum local workers.
62-
/// </param>
61+
/// <param name="maxLocalWorkers">The maximum local workers.</param>
6362
public StatelessWorkerAttribute(int maxLocalWorkers)
6463
: base(new StatelessWorkerPlacement(maxLocalWorkers))
6564
{
6665
}
6766

67+
/// <summary>
68+
/// Initializes a new instance of the <see cref="StatelessWorkerAttribute"/> class.
69+
/// </summary>
70+
/// <param name="maxLocalWorkers">The maximum local workers.</param>
71+
public StatelessWorkerAttribute(int maxLocalWorkers, bool removeIdleWorkers)
72+
: base(new StatelessWorkerPlacement(maxLocalWorkers, removeIdleWorkers))
73+
{
74+
}
75+
6876
/// <summary>
6977
/// Initializes a new instance of the <see cref="StatelessWorkerAttribute"/> class.
7078
/// </summary>

src/Orleans.Core.Abstractions/Core/IGrainBase.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,14 +290,19 @@ public enum DeactivationReasonCode : byte
290290
ApplicationError,
291291

292292
/// <summary>
293-
/// The application requested that this activation deactivate.
293+
/// The application requested to deactivate this activation.
294294
/// </summary>
295295
ApplicationRequested,
296296

297297
/// <summary>
298298
/// This activation is migrating to a new location.
299299
/// </summary>
300300
Migrating,
301+
302+
/// <summary>
303+
/// The runtime requested to deactivate this activation.
304+
/// </summary>
305+
RuntimeRequested
301306
}
302307

303308
internal static class DeactivationReasonCodeExtensions

src/Orleans.Core.Abstractions/Placement/StatelessWorkerPlacement.cs

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ namespace Orleans.Runtime
1212
internal sealed class StatelessWorkerPlacement : PlacementStrategy
1313
{
1414
private const string MaxLocalPropertyKey = "max-local-instances";
15+
private const string RemoveIdleWorkersPropertyKey = "remove-idle-workers";
16+
1517
private static readonly int DefaultMaxStatelessWorkers = Environment.ProcessorCount;
1618

1719
/// <inheritdoc/>
@@ -23,17 +25,38 @@ internal sealed class StatelessWorkerPlacement : PlacementStrategy
2325
[Id(0)]
2426
public int MaxLocal { get; private set; }
2527

28+
/// <summary>
29+
/// When set to <see langword="true"/>, idle workers will be proactively deactivated by the runtime.
30+
/// Otherwise if <see langword="false"/>, than the workers will be deactivated according to collection age.
31+
/// </summary>
32+
[Id(1)]
33+
public bool RemoveIdleWorkers { get; private set; } = true;
34+
35+
/// <summary>
36+
/// Initializes a new instance of the <see cref="StatelessWorkerPlacement"/> class.
37+
/// </summary>
38+
/// <param name="maxLocal">
39+
/// The maximum number of local instances which can be simultaneously active for a given grain.
40+
/// </param>
41+
internal StatelessWorkerPlacement(int maxLocal) : this(maxLocal, true)
42+
{
43+
}
44+
2645
/// <summary>
2746
/// Initializes a new instance of the <see cref="StatelessWorkerPlacement"/> class.
2847
/// </summary>
2948
/// <param name="maxLocal">
3049
/// The maximum number of local instances which can be simultaneously active for a given grain.
3150
/// </param>
32-
internal StatelessWorkerPlacement(int maxLocal)
51+
/// <param name="removeIdleWorkers">
52+
/// Whether idle workers will be proactively deactivated by the runtime instead of only being deactivated according to collection age.
53+
/// </param>
54+
internal StatelessWorkerPlacement(int maxLocal, bool removeIdleWorkers)
3355
{
3456
// If maxLocal was not specified on the StatelessWorkerAttribute,
3557
// we will use the defaultMaxStatelessWorkers, which is System.Environment.ProcessorCount.
3658
this.MaxLocal = maxLocal > 0 ? maxLocal : DefaultMaxStatelessWorkers;
59+
this.RemoveIdleWorkers = removeIdleWorkers;
3760
}
3861

3962
/// <summary>
@@ -44,28 +67,39 @@ public StatelessWorkerPlacement() : this(-1)
4467
}
4568

4669
/// <inheritdoc/>
47-
public override string ToString() => $"StatelessWorkerPlacement(max={MaxLocal})";
70+
public override string ToString() => $"StatelessWorkerPlacement(MaxLocal={MaxLocal}, RemoveIdleWorkers={RemoveIdleWorkers})";
4871

4972
/// <inheritdoc/>
5073
public override void Initialize(GrainProperties properties)
5174
{
5275
base.Initialize(properties);
53-
if (properties.Properties.TryGetValue(MaxLocalPropertyKey, out var value)
54-
&& !string.IsNullOrWhiteSpace(value))
76+
77+
if (properties.Properties.TryGetValue(MaxLocalPropertyKey, out var maxLocalValue) &&
78+
!string.IsNullOrWhiteSpace(maxLocalValue))
79+
{
80+
if (int.TryParse(maxLocalValue, out var maxLocal))
81+
{
82+
MaxLocal = maxLocal;
83+
}
84+
}
85+
86+
if (properties.Properties.TryGetValue(RemoveIdleWorkersPropertyKey, out var removeIdleValue) &&
87+
!string.IsNullOrWhiteSpace(removeIdleValue))
5588
{
56-
if (int.TryParse(value, out var maxLocal))
89+
if (bool.TryParse(removeIdleValue, out var removeIdle))
5790
{
58-
this.MaxLocal = maxLocal;
91+
RemoveIdleWorkers = removeIdle;
5992
}
6093
}
6194
}
6295

6396
/// <inheritdoc/>
6497
public override void PopulateGrainProperties(IServiceProvider services, Type grainClass, GrainType grainType, Dictionary<string, string> properties)
6598
{
66-
properties[MaxLocalPropertyKey] = this.MaxLocal.ToString(CultureInfo.InvariantCulture);
99+
properties[MaxLocalPropertyKey] = MaxLocal.ToString(CultureInfo.InvariantCulture);
100+
properties[RemoveIdleWorkersPropertyKey] = RemoveIdleWorkers.ToString(CultureInfo.InvariantCulture);
67101

68102
base.PopulateGrainProperties(services, grainClass, grainType, properties);
69103
}
70104
}
71-
}
105+
}

src/Orleans.Persistence.Memory/Storage/MemoryStorageWithLatency.cs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ public MemoryGrainStorageWithLatency(
5252
MemoryStorageWithLatencyOptions options,
5353
ILoggerFactory loggerFactory,
5454
IGrainFactory grainFactory,
55-
IGrainStorageSerializer defaultGrainStorageSerialzier)
55+
IGrainStorageSerializer defaultGrainStorageSerializer)
5656
{
57-
this.baseGranStorage = new MemoryGrainStorage(name, options, loggerFactory.CreateLogger<MemoryGrainStorage>(), grainFactory, defaultGrainStorageSerialzier);
57+
this.baseGranStorage = new MemoryGrainStorage(name, options, loggerFactory.CreateLogger<MemoryGrainStorage>(), grainFactory, defaultGrainStorageSerializer);
5858
this.options = options;
5959
}
6060

@@ -101,16 +101,24 @@ private async Task MakeFixedLatencyCall(Func<Task> action)
101101
error = exc;
102102
}
103103

104-
if (sw.Elapsed < this.options.Latency)
104+
do
105105
{
106106
// Work out the remaining time to wait so that this operation exceeds the required Latency.
107107
// Also adds an extra fudge factor to account for any system clock resolution edge cases.
108108
var extraDelay = TimeSpan.FromTicks(
109-
this.options.Latency.Ticks - sw.Elapsed.Ticks + TimeSpan.TicksPerMillisecond /* round up */ );
109+
5 * TimeSpan.TicksPerMillisecond + this.options.Latency.Ticks - sw.Elapsed.Ticks);
110110

111-
await Task.Delay(extraDelay);
112-
}
111+
if (extraDelay > TimeSpan.Zero)
112+
{
113+
await Task.Delay(extraDelay);
114+
}
115+
else
116+
{
117+
break;
118+
}
119+
} while (true);
113120

121+
Debug.Assert(sw.Elapsed >= this.options.Latency, "sw.Elapsed >= this.options.Latency");
114122
if (error != null)
115123
{
116124
// Wrap in AggregateException so that the original error stack trace is preserved.

src/Orleans.Runtime/Catalog/GrainTypeSharedContext.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public GrainTypeSharedContext(
3333
IOptions<SiloMessagingOptions> messagingOptions,
3434
IOptions<GrainCollectionOptions> collectionOptions,
3535
IOptions<SchedulingOptions> schedulingOptions,
36+
IOptions<StatelessWorkerOptions> statelessWorkerOptions,
3637
IGrainRuntime grainRuntime,
3738
ILoggerFactory loggerFactory,
3839
GrainReferenceActivator grainReferenceActivator,
@@ -56,6 +57,7 @@ public GrainTypeSharedContext(
5657
var grainDirectoryResolver = serviceProvider.GetRequiredService<GrainDirectoryResolver>();
5758
GrainDirectory = PlacementStrategy.IsUsingGrainDirectory ? grainDirectoryResolver.Resolve(grainType) : null;
5859
SchedulingOptions = schedulingOptions.Value;
60+
StatelessWorkerOptions = statelessWorkerOptions.Value;
5961
Runtime = grainRuntime;
6062
MigrationManager = _serviceProvider.GetService<IActivationMigrationManager>();
6163

@@ -181,6 +183,11 @@ public void SetComponent<TComponent>(TComponent? instance)
181183
/// </summary>
182184
public SchedulingOptions SchedulingOptions { get; }
183185

186+
/// <summary>
187+
/// Gets the stateless worker options.
188+
/// </summary>
189+
public StatelessWorkerOptions StatelessWorkerOptions { get; }
190+
184191
/// <summary>
185192
/// Gets the grain runtime.
186193
/// </summary>

0 commit comments

Comments
 (0)