mirror of https://github.com/dapr/dotnet-sdk.git
Add support for per type actor configuration (#870)
This commit allows different actor types to provide their own configurations instead of relying on the top-level values. Any value defined in them will be used instead of the top-level. Anything that is left out will use the top-level or default if it is undefined. https://github.com/dapr/dotnet-sdk/issues/857 Signed-off-by: Hal Spang <halspang@microsoft.com>
This commit is contained in:
parent
cda60cb382
commit
e4236c4c54
|
@ -23,9 +23,19 @@ namespace Dapr.Actors.Runtime
|
|||
/// Initializes a new instance of <see cref="ActorRegistration" />.
|
||||
/// </summary>
|
||||
/// <param name="type">The <see cref="ActorTypeInformation" /> for the actor type.</param>
|
||||
public ActorRegistration(ActorTypeInformation type)
|
||||
public ActorRegistration(ActorTypeInformation type) : this(type, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="ActorRegistration" />.
|
||||
/// </summary>
|
||||
/// <param name="type">The <see cref="ActorTypeInformation" /> for the actor type.</param>
|
||||
/// <param name="options">The optional <see cref="ActorRuntimeOptions"/> that are specified for this type only.</param>
|
||||
public ActorRegistration(ActorTypeInformation type, ActorRuntimeOptions options)
|
||||
{
|
||||
this.Type = type;
|
||||
this.TypeOptions = options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -38,5 +48,10 @@ namespace Dapr.Actors.Runtime
|
|||
/// activator of the runtime will be used.
|
||||
/// </summary>
|
||||
public ActorActivator Activator { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// An optional set of options for this specific actor type. These will override the top level or default values.
|
||||
/// </summary>
|
||||
public ActorRuntimeOptions TypeOptions { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,18 @@ namespace Dapr.Actors.Runtime
|
|||
RegisterActor<TActor>(actorTypeName: null, configure);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers an actor type in the collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="TActor">Type of actor.</typeparam>
|
||||
/// <param name="typeOptions">An optional <see cref="ActorRuntimeOptions"/> that defines values for this type alone.</param>
|
||||
/// <param name="configure">An optional delegate used to configure the actor registration.</param>
|
||||
public void RegisterActor<TActor>(ActorRuntimeOptions typeOptions, Action<ActorRegistration> configure = null)
|
||||
where TActor : Actor
|
||||
{
|
||||
RegisterActor<TActor>(null, typeOptions, configure);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers an actor type in the collection.
|
||||
/// </summary>
|
||||
|
@ -51,9 +63,23 @@ namespace Dapr.Actors.Runtime
|
|||
/// <remarks>The value of <paramref name="actorTypeName"/> will have precedence over the default actor type name derived from the actor implementation type or any type name set via <see cref="ActorAttribute"/>.</remarks>
|
||||
public void RegisterActor<TActor>(string actorTypeName, Action<ActorRegistration> configure = null)
|
||||
where TActor : Actor
|
||||
{
|
||||
RegisterActor<TActor>(actorTypeName, null, configure);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers an actor type in the collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="TActor">Type of actor.</typeparam>
|
||||
/// <param name="actorTypeName">The name of the actor type represented by the actor.</param>
|
||||
/// <param name="typeOptions">An optional <see cref="ActorRuntimeOptions"/> that defines values for this type alone.</param>
|
||||
/// <param name="configure">An optional delegate used to configure the actor registration.</param>
|
||||
/// <remarks>The value of <paramref name="actorTypeName"/> will have precedence over the default actor type name derived from the actor implementation type or any type name set via <see cref="ActorAttribute"/>.</remarks>
|
||||
public void RegisterActor<TActor>(string actorTypeName, ActorRuntimeOptions typeOptions, Action<ActorRegistration> configure = null)
|
||||
where TActor : Actor
|
||||
{
|
||||
var actorTypeInfo = ActorTypeInformation.Get(typeof(TActor), actorTypeName);
|
||||
var registration = new ActorRegistration(actorTypeInfo);
|
||||
var registration = new ActorRegistration(actorTypeInfo, typeOptions);
|
||||
configure?.Invoke(registration);
|
||||
this.Add(registration);
|
||||
}
|
||||
|
|
|
@ -11,18 +11,19 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Actors.Client;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Dapr.Actors.Runtime
|
||||
{
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Actors.Client;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// Contains methods to register actor types. Registering the types allows the runtime to create instances of the actor.
|
||||
/// </summary>
|
||||
|
@ -80,46 +81,72 @@ namespace Dapr.Actors.Runtime
|
|||
|
||||
writer.WriteEndArray();
|
||||
|
||||
if (this.options.ActorIdleTimeout != null)
|
||||
{
|
||||
writer.WriteString("actorIdleTimeout", ConverterUtils.ConvertTimeSpanValueInDaprFormat(this.options.ActorIdleTimeout));
|
||||
}
|
||||
writeActorOptions(writer, this.options);
|
||||
|
||||
if (this.options.ActorScanInterval != null)
|
||||
{
|
||||
writer.WriteString("actorScanInterval", ConverterUtils.ConvertTimeSpanValueInDaprFormat(this.options.ActorScanInterval));
|
||||
}
|
||||
var actorsWithConfigs = this.options.Actors.Where(actor => actor.TypeOptions != null).ToList();
|
||||
|
||||
if (this.options.DrainOngoingCallTimeout != null)
|
||||
if (actorsWithConfigs.Count > 0)
|
||||
{
|
||||
writer.WriteString("drainOngoingCallTimeout", ConverterUtils.ConvertTimeSpanValueInDaprFormat(this.options.DrainOngoingCallTimeout));
|
||||
}
|
||||
writer.WritePropertyName("entitiesConfig");
|
||||
writer.WriteStartArray();
|
||||
foreach (var actor in actorsWithConfigs)
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
writer.WritePropertyName("entities");
|
||||
writer.WriteStartArray();
|
||||
writer.WriteStringValue(actor.Type.ActorTypeName);
|
||||
writer.WriteEndArray();
|
||||
|
||||
// default is false, don't write it if default
|
||||
if (this.options.DrainRebalancedActors != false)
|
||||
{
|
||||
writer.WriteBoolean("drainRebalancedActors", (this.options.DrainRebalancedActors));
|
||||
}
|
||||
writeActorOptions(writer, actor.TypeOptions);
|
||||
|
||||
// default is null, don't write it if default
|
||||
if (this.options.RemindersStoragePartitions != null)
|
||||
{
|
||||
writer.WriteNumber("remindersStoragePartitions", this.options.RemindersStoragePartitions.Value);
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
writer.WriteEndArray();
|
||||
}
|
||||
|
||||
// Reentrancy has a default value so it is always included.
|
||||
writer.WriteStartObject("reentrancy");
|
||||
writer.WriteBoolean("enabled", this.options.ReentrancyConfig.Enabled);
|
||||
if (this.options.ReentrancyConfig.MaxStackDepth != null)
|
||||
{
|
||||
writer.WriteNumber("maxStackDepth", this.options.ReentrancyConfig.MaxStackDepth.Value);
|
||||
}
|
||||
writer.WriteEndObject();
|
||||
|
||||
writer.WriteEndObject();
|
||||
return writer.FlushAsync();
|
||||
}
|
||||
|
||||
private void writeActorOptions(Utf8JsonWriter writer, ActorRuntimeOptions actorOptions)
|
||||
{
|
||||
if (actorOptions.ActorIdleTimeout != null)
|
||||
{
|
||||
writer.WriteString("actorIdleTimeout", ConverterUtils.ConvertTimeSpanValueInDaprFormat(actorOptions.ActorIdleTimeout));
|
||||
}
|
||||
|
||||
if (actorOptions.ActorScanInterval != null)
|
||||
{
|
||||
writer.WriteString("actorScanInterval", ConverterUtils.ConvertTimeSpanValueInDaprFormat(actorOptions.ActorScanInterval));
|
||||
}
|
||||
|
||||
if (actorOptions.DrainOngoingCallTimeout != null)
|
||||
{
|
||||
writer.WriteString("drainOngoingCallTimeout", ConverterUtils.ConvertTimeSpanValueInDaprFormat(actorOptions.DrainOngoingCallTimeout));
|
||||
}
|
||||
|
||||
// default is false, don't write it if default
|
||||
if (actorOptions.DrainRebalancedActors != false)
|
||||
{
|
||||
writer.WriteBoolean("drainRebalancedActors", (actorOptions.DrainRebalancedActors));
|
||||
}
|
||||
|
||||
// default is null, don't write it if default
|
||||
if (actorOptions.RemindersStoragePartitions != null)
|
||||
{
|
||||
writer.WriteNumber("remindersStoragePartitions", actorOptions.RemindersStoragePartitions.Value);
|
||||
}
|
||||
|
||||
// Reentrancy has a default value so it is always included.
|
||||
writer.WriteStartObject("reentrancy");
|
||||
writer.WriteBoolean("enabled", actorOptions.ReentrancyConfig.Enabled);
|
||||
if (actorOptions.ReentrancyConfig.MaxStackDepth != null)
|
||||
{
|
||||
writer.WriteNumber("maxStackDepth", actorOptions.ReentrancyConfig.MaxStackDepth.Value);
|
||||
}
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
// Deactivates an actor for an actor type with given actor id.
|
||||
internal async Task DeactivateAsync(string actorTypeName, string actorId)
|
||||
{
|
||||
|
|
|
@ -262,6 +262,60 @@ namespace Dapr.Actors.Test
|
|||
Assert.Equal(64, element.GetInt32());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TestActorSettingsWithPerActorConfigurations()
|
||||
{
|
||||
var actorType = typeof(TestActor);
|
||||
var options = new ActorRuntimeOptions();
|
||||
options.ActorIdleTimeout = TimeSpan.FromSeconds(33);
|
||||
options.ActorScanInterval = TimeSpan.FromSeconds(44);
|
||||
options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(55);
|
||||
options.DrainRebalancedActors = true;
|
||||
options.ReentrancyConfig.Enabled = true;
|
||||
options.ReentrancyConfig.MaxStackDepth = 32;
|
||||
options.Actors.RegisterActor<TestActor>(options);
|
||||
|
||||
var runtime = new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory);
|
||||
|
||||
Assert.Contains(actorType.Name, runtime.RegisteredActors.Select(a => a.Type.ActorTypeName), StringComparer.InvariantCulture);
|
||||
|
||||
ArrayBufferWriter<byte> writer = new ArrayBufferWriter<byte>();
|
||||
await runtime.SerializeSettingsAndRegisteredTypes(writer);
|
||||
|
||||
// read back the serialized json
|
||||
var array = writer.WrittenSpan.ToArray();
|
||||
string s = Encoding.UTF8.GetString(array, 0, array.Length);
|
||||
|
||||
JsonDocument document = JsonDocument.Parse(s);
|
||||
JsonElement root = document.RootElement;
|
||||
|
||||
JsonElement element = root.GetProperty("entities");
|
||||
Assert.Equal(1, element.GetArrayLength());
|
||||
|
||||
element = root.GetProperty("entitiesConfig");
|
||||
Assert.Equal(1, element.GetArrayLength());
|
||||
|
||||
var perEntityConfig = element[0];
|
||||
|
||||
element = perEntityConfig.GetProperty("actorIdleTimeout");
|
||||
Assert.Equal(TimeSpan.FromSeconds(33), ConverterUtils.ConvertTimeSpanFromDaprFormat(element.GetString()));
|
||||
|
||||
element = perEntityConfig.GetProperty("actorScanInterval");
|
||||
Assert.Equal(TimeSpan.FromSeconds(44), ConverterUtils.ConvertTimeSpanFromDaprFormat(element.GetString()));
|
||||
|
||||
element = perEntityConfig.GetProperty("drainOngoingCallTimeout");
|
||||
Assert.Equal(TimeSpan.FromSeconds(55), ConverterUtils.ConvertTimeSpanFromDaprFormat(element.GetString()));
|
||||
|
||||
element = perEntityConfig.GetProperty("drainRebalancedActors");
|
||||
Assert.True(element.GetBoolean());
|
||||
|
||||
element = root.GetProperty("reentrancy").GetProperty("enabled");
|
||||
Assert.True(element.GetBoolean());
|
||||
|
||||
element = root.GetProperty("reentrancy").GetProperty("maxStackDepth");
|
||||
Assert.Equal(32, element.GetInt32());
|
||||
}
|
||||
|
||||
private sealed class TestActor : Actor, ITestActor
|
||||
{
|
||||
public TestActor(ActorHost host)
|
||||
|
|
|
@ -27,8 +27,15 @@ namespace Dapr.E2E.Test.App.ReentrantActors
|
|||
{
|
||||
services.AddActors(options =>
|
||||
{
|
||||
options.Actors.RegisterActor<ReentrantActor>();
|
||||
options.ReentrancyConfig = new() { Enabled = true };
|
||||
// We force this to use a per-actor config as an easy way to validate that's working.
|
||||
options.ReentrancyConfig = new() { Enabled = false };
|
||||
options.Actors.RegisterActor<ReentrantActor>(typeOptions: new()
|
||||
{
|
||||
ReentrancyConfig = new()
|
||||
{
|
||||
Enabled = true,
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue