Skip to content

Commit 20fabb2

Browse files
authored
Support more actor configuration (#269)
* 219 Support the granular Actor config such as actor idle time * cleanup * cr * cr * refactor * remove now dead code * Move setter into properties Co-authored-by: LM <lemai>
1 parent 5732c72 commit 20fabb2

File tree

4 files changed

+232
-24
lines changed

4 files changed

+232
-24
lines changed

Diff for: src/Dapr.Actors.AspNetCore/RouterBuilderExtensions.cs

+2-22
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
66
namespace Dapr.Actors.AspNetCore
77
{
88
using System;
9-
using System.Buffers;
10-
using System.Text.Json;
11-
using System.Threading.Tasks;
129
using Dapr.Actors.Runtime;
1310
using Microsoft.AspNetCore.Http;
1411
using Microsoft.AspNetCore.Routing;
@@ -19,7 +16,7 @@ public static void AddDaprConfigRoute(this IRouteBuilder routeBuilder)
1916
{
2017
routeBuilder.MapGet("dapr/config", async context =>
2118
{
22-
await WriteSupportedActorTypesAsJsonAsync(context.Response.BodyWriter);
19+
await ActorRuntime.Instance.SerializeSettingsAndRegisteredTypes(context.Response.BodyWriter);
2320
});
2421
}
2522

@@ -123,23 +120,6 @@ public static void AddTimerRoute(this IRouteBuilder routeBuilder)
123120
// read dueTime, period and data from Request Body.
124121
await ActorRuntime.FireTimerAsync(actorTypeName, actorId, timerName);
125122
});
126-
}
127-
128-
private static async Task WriteSupportedActorTypesAsJsonAsync(IBufferWriter<byte> output)
129-
{
130-
using Utf8JsonWriter writer = new Utf8JsonWriter(output);
131-
writer.WriteStartObject();
132-
writer.WritePropertyName("entities");
133-
writer.WriteStartArray();
134-
135-
foreach (var actorType in ActorRuntime.Instance.RegisteredActorTypes)
136-
{
137-
writer.WriteStringValue(actorType);
138-
}
139-
140-
writer.WriteEndArray();
141-
writer.WriteEndObject();
142-
await writer.FlushAsync();
143-
}
123+
}
144124
}
145125
}

Diff for: src/Dapr.Actors/Runtime/ActorRuntime.cs

+53
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ namespace Dapr.Actors.Runtime
88
using System;
99
using System.Collections.Generic;
1010
using System.IO;
11+
using System.Text.Json;
1112
using System.Threading;
1213
using System.Threading.Tasks;
1314

@@ -26,12 +27,15 @@ public class ActorRuntime
2627
// Map of ActorType --> ActorManager.
2728
private readonly Dictionary<string, ActorManager> actorManagers = new Dictionary<string, ActorManager>();
2829

30+
private ActorSettings actorSettings;
31+
2932
/// <remarks>
3033
/// WARNING: This type is expected to be accessed via the <see cref="Instance" /> singleton instance.
3134
/// This constructor is exposed only for unit testing purposes.
3235
/// </remarks>
3336
internal ActorRuntime()
3437
{
38+
this.actorSettings = new ActorSettings();
3539
}
3640

3741
/// <summary>
@@ -65,6 +69,55 @@ public void RegisterActor<TActor>(Func<ActorTypeInformation, ActorService> actor
6569
this.actorManagers[actorTypeInfo.ActorTypeName] = new ActorManager(actorService);
6670
}
6771

72+
/// <summary>
73+
/// Allows configuration of this app's actor configuration.
74+
/// </summary>
75+
/// <param name="actorSettingsDelegate">A delegate to edit the default ActorSettings object.</param>
76+
public void ConfigureActorSettings(Action<ActorSettings> actorSettingsDelegate)
77+
{
78+
actorSettingsDelegate.Invoke(this.actorSettings);
79+
}
80+
81+
internal Task SerializeSettingsAndRegisteredTypes(System.Buffers.IBufferWriter<byte> output)
82+
{
83+
using Utf8JsonWriter writer = new Utf8JsonWriter(output);
84+
writer.WriteStartObject();
85+
86+
writer.WritePropertyName("entities");
87+
writer.WriteStartArray();
88+
89+
foreach (var actorType in this.RegisteredActorTypes)
90+
{
91+
writer.WriteStringValue(actorType);
92+
}
93+
94+
writer.WriteEndArray();
95+
96+
if (this.actorSettings.ActorIdleTimeout != null)
97+
{
98+
writer.WriteString("actorIdleTimeout", ConverterUtils.ConvertTimeSpanValueInDaprFormat(this.actorSettings.ActorIdleTimeout));
99+
}
100+
101+
if (this.actorSettings.ActorScanInterval != null)
102+
{
103+
writer.WriteString("actorScanInterval", ConverterUtils.ConvertTimeSpanValueInDaprFormat(this.actorSettings.ActorScanInterval));
104+
}
105+
106+
if (this.actorSettings.DrainOngoingCallTimeout != null)
107+
{
108+
writer.WriteString("drainOngoingCallTimeout", ConverterUtils.ConvertTimeSpanValueInDaprFormat(this.actorSettings.DrainOngoingCallTimeout));
109+
}
110+
111+
// default is false, don't write it if default
112+
if (this.actorSettings.DrainRebalancedActors != false)
113+
{
114+
writer.WriteBoolean("drainRebalancedActors", (this.actorSettings.DrainRebalancedActors));
115+
}
116+
117+
writer.WriteEndObject();
118+
return writer.FlushAsync();
119+
}
120+
68121
/// <summary>
69122
/// Activates an actor for an actor type with given actor id.
70123
/// </summary>

Diff for: src/Dapr.Actors/Runtime/ActorSettings.cs

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// ------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation.
3+
// Licensed under the MIT License.
4+
// ------------------------------------------------------------
5+
6+
namespace Dapr.Actors.Runtime
7+
{
8+
using System;
9+
using System.Text.Json;
10+
11+
/// <summary>
12+
/// Represents Dapr actor configuration for this app. See
13+
/// https://github.com/dapr/docs/blob/master/reference/api/actors_api.md
14+
/// </summary>
15+
public sealed class ActorSettings
16+
{
17+
private TimeSpan? actorIdleTimeout;
18+
private TimeSpan? actorScanInterval;
19+
private TimeSpan? drainOngoingCallTimeout;
20+
private bool drainRebalancedActors;
21+
22+
/// <summary>
23+
/// Default constructor.
24+
/// </summary>
25+
public ActorSettings()
26+
{
27+
}
28+
29+
/// <summary>
30+
/// Specifies how long to wait before deactivating an idle actor. An actor is idle
31+
/// if no actor method calls and no reminders have fired on it.
32+
/// See https://github.com/dapr/docs/blob/master/reference/api/actors_api.md#dapr-calling-to-user-service-code
33+
/// for more including default values.
34+
/// </summary>
35+
public TimeSpan? ActorIdleTimeout
36+
{
37+
get
38+
{
39+
return this.actorIdleTimeout;
40+
}
41+
42+
set
43+
{
44+
if (value <= TimeSpan.Zero)
45+
{
46+
throw new ArgumentOutOfRangeException(nameof(actorIdleTimeout), actorIdleTimeout, "must be positive");
47+
}
48+
49+
this.actorIdleTimeout = value;
50+
}
51+
}
52+
53+
/// <summary>
54+
/// A duration which specifies how often to scan for actors to deactivate idle actors.
55+
/// Actors that have been idle longer than the actorIdleTimeout will be deactivated.
56+
/// See https://github.com/dapr/docs/blob/master/reference/api/actors_api.md#dapr-calling-to-user-service-code
57+
/// for more including default values.
58+
/// </summary>
59+
public TimeSpan? ActorScanInterval
60+
{
61+
get
62+
{
63+
return this.actorScanInterval;
64+
}
65+
66+
set
67+
{
68+
if (value <= TimeSpan.Zero)
69+
{
70+
throw new ArgumentOutOfRangeException(nameof(actorScanInterval), actorScanInterval, "must be positive");
71+
}
72+
73+
this.actorScanInterval = value;
74+
}
75+
}
76+
77+
/// <summary>
78+
/// A duration used when in the process of draining rebalanced actors. This specifies
79+
/// how long to wait for the current active actor method to finish. If there is no current actor method call, this is ignored.
80+
/// See https://github.com/dapr/docs/blob/master/reference/api/actors_api.md#dapr-calling-to-user-service-code
81+
/// for more including default values.
82+
/// </summary>
83+
public TimeSpan? DrainOngoingCallTimeout
84+
{
85+
get
86+
{
87+
return this.drainOngoingCallTimeout;
88+
}
89+
90+
set
91+
{
92+
if (value <= TimeSpan.Zero)
93+
{
94+
throw new ArgumentOutOfRangeException(nameof(drainOngoingCallTimeout), drainOngoingCallTimeout, "must be positive");
95+
}
96+
97+
this.drainOngoingCallTimeout = value;
98+
}
99+
}
100+
101+
/// <summary>
102+
/// A bool. If true, Dapr will wait for drainOngoingCallTimeout to allow a current
103+
/// actor call to complete before trying to deactivate an actor. If false, do not wait.
104+
/// See https://github.com/dapr/docs/blob/master/reference/api/actors_api.md#dapr-calling-to-user-service-code
105+
/// for more including default values.
106+
/// </summary>
107+
public bool DrainRebalancedActors
108+
{
109+
get
110+
{
111+
return this.drainRebalancedActors;
112+
}
113+
114+
set
115+
{
116+
this.drainRebalancedActors = value;
117+
}
118+
}
119+
}
120+
}

Diff for: test/Dapr.Actors.Test/Runtime/ActorRuntimeTests.cs

+57-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
// ------------------------------------------------------------
1+
// ------------------------------------------------------------
22
// Copyright (c) Microsoft Corporation.
33
// Licensed under the MIT License.
44
// ------------------------------------------------------------
55

66
namespace Dapr.Actors.Test
77
{
88
using System;
9+
using System.Buffers;
10+
using System.Text;
11+
using System.Text.Json;
912
using Dapr.Actors;
1013
using Dapr.Actors.Runtime;
1114
using Xunit;
@@ -46,6 +49,58 @@ public void TestExplicitActorType()
4649
Assert.Contains(RenamedActorTypeName, actorRuntime.RegisteredActorTypes, StringComparer.InvariantCulture);
4750
}
4851

52+
[Fact]
53+
public void TestActorSettings()
54+
{
55+
var actorType = typeof(TestActor);
56+
var actorRuntime = new ActorRuntime();
57+
58+
Assert.Empty(actorRuntime.RegisteredActorTypes);
59+
60+
actorRuntime.ConfigureActorSettings(a =>
61+
{
62+
a.ActorIdleTimeout = TimeSpan.FromSeconds(33);
63+
a.ActorScanInterval = TimeSpan.FromSeconds(44);
64+
a.DrainOngoingCallTimeout = TimeSpan.FromSeconds(55);
65+
a.DrainRebalancedActors = true;
66+
});
67+
68+
actorRuntime.RegisterActor<TestActor>();
69+
70+
Assert.Contains(actorType.Name, actorRuntime.RegisteredActorTypes, StringComparer.InvariantCulture);
71+
72+
ArrayBufferWriter<byte> writer = new ArrayBufferWriter<byte>();
73+
actorRuntime.SerializeSettingsAndRegisteredTypes(writer).GetAwaiter().GetResult();
74+
75+
// read back the serialized json
76+
var array = writer.WrittenSpan.ToArray();
77+
string s = Encoding.UTF8.GetString(array, 0, array.Length);
78+
79+
JsonDocument document = JsonDocument.Parse(s);
80+
JsonElement root = document.RootElement;
81+
82+
// parse out the entities array
83+
JsonElement element = root.GetProperty("entities");
84+
Assert.Equal(1, element.GetArrayLength());
85+
86+
JsonElement arrayElement = element[0];
87+
string actor = arrayElement.GetString();
88+
Assert.Equal("TestActor", actor);
89+
90+
// validate the other properties have expected values
91+
element = root.GetProperty("actorIdleTimeout");
92+
Assert.Equal(TimeSpan.FromSeconds(33), ConverterUtils.ConvertTimeSpanFromDaprFormat(element.GetString()));
93+
94+
element = root.GetProperty("actorScanInterval");
95+
Assert.Equal(TimeSpan.FromSeconds(44), ConverterUtils.ConvertTimeSpanFromDaprFormat(element.GetString()));
96+
97+
element = root.GetProperty("drainOngoingCallTimeout");
98+
Assert.Equal(TimeSpan.FromSeconds(55), ConverterUtils.ConvertTimeSpanFromDaprFormat(element.GetString()));
99+
100+
element = root.GetProperty("drainRebalancedActors");
101+
Assert.True(element.GetBoolean());
102+
}
103+
49104
private sealed class TestActor : Actor, ITestActor
50105
{
51106
public TestActor(ActorService actorService, ActorId actorId)
@@ -63,4 +118,4 @@ public RenamedActor(ActorService actorService, ActorId actorId)
63118
}
64119
}
65120
}
66-
}
121+
}

0 commit comments

Comments
 (0)