Skip to content

Commit

Permalink
Expand Remote and Cluster options (#366)
Browse files Browse the repository at this point in the history
* Expand Remote and Cluster options

* Update API approval list

---------

Co-authored-by: Aaron Stannard <[email protected]>
  • Loading branch information
Arkatufus and Aaronontheweb authored Aug 30, 2023
1 parent bbabe2a commit 6fd3472
Show file tree
Hide file tree
Showing 10 changed files with 555 additions and 49 deletions.
23 changes: 23 additions & 0 deletions src/Akka.Cluster.Hosting.Tests/ClusterOptionsSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
using Akka.Cluster.SBR;
using Akka.Configuration;
using Akka.Hosting;
using Akka.Remote.Hosting;
using FluentAssertions;
using FluentAssertions.Extensions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -42,6 +44,8 @@ public void EmptyClusterOptionsTest()
settings.LogInfo.Should().BeTrue();
settings.LogInfoVerbose.Should().BeFalse();
settings.DowningProviderType.Should().Be(typeof(SplitBrainResolverProvider));
settings.HeartbeatInterval.Should().Be(1.Seconds());
settings.HeartbeatExpectedResponseAfter.Should().Be(1.Seconds());
}

[Fact(DisplayName = "ClusterOptions should generate proper HOCON values")]
Expand All @@ -64,6 +68,16 @@ public void ClusterOptionsTest()
SplitBrainResolver = new KeepMajorityOption
{
Role = "back-end"
},
FailureDetector = new PhiAccrualFailureDetectorOptions
{
HeartbeatInterval = 1.1.Seconds(),
AcceptableHeartbeatPause = 1.1.Seconds(),
Threshold = 1.1,
MaxSampleSize = 1,
MinStandardDeviation = 1.1.Seconds(),
UnreachableNodesReaperInterval = 1.1.Seconds(),
ExpectedResponseAfter = 1.1.Seconds()
}
});

Expand All @@ -86,6 +100,15 @@ public void ClusterOptionsTest()
var sbrConfig = builder.Configuration.Value.GetConfig("akka.cluster.split-brain-resolver");
sbrConfig.GetString("active-strategy").Should().Be(SplitBrainResolverSettings.KeepMajorityName);
sbrConfig.GetString($"{SplitBrainResolverSettings.KeepMajorityName}.role").Should().Be("back-end");

var detectorConfig = builder.Configuration.Value.GetConfig("akka.cluster.failure-detector");
detectorConfig.GetTimeSpan("heartbeat-interval").Should().Be(1.1.Seconds());
detectorConfig.GetTimeSpan("acceptable-heartbeat-pause").Should().Be(1.1.Seconds());
detectorConfig.GetDouble("threshold").Should().Be(1.1);
detectorConfig.GetInt("max-sample-size").Should().Be(1);
detectorConfig.GetTimeSpan("min-std-deviation").Should().Be(1.1.Seconds());
detectorConfig.GetTimeSpan("unreachable-nodes-reaper-interval").Should().Be(1.1.Seconds());
detectorConfig.GetTimeSpan("expected-response-after").Should().Be(1.1.Seconds());
}

[Fact(DisplayName = "ClusterOptions should be bindable using Microsoft.Extensions.Configuration")]
Expand Down
71 changes: 45 additions & 26 deletions src/Akka.Cluster.Hosting/AkkaClusterHostingExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
using Akka.Hosting;
using Akka.Hosting.Coordination;
using Akka.Persistence.Hosting;
using Akka.Remote;
using Akka.Remote.Hosting;
using Akka.Util;

#nullable enable
Expand Down Expand Up @@ -114,6 +116,13 @@ public sealed class ClusterOptions
/// uses the keep majority resolving strategy.
/// </summary>
public SplitBrainResolverOption? SplitBrainResolver { get; set; }

/// <summary>
/// <para>
/// Settings for the failure detector used by the cluster subsystem to detect unreachable members.
/// </para>
/// </summary>
public PhiAccrualFailureDetectorOptions? FailureDetector { get; set; }
}

public sealed class ClusterSingletonOptions
Expand Down Expand Up @@ -299,39 +308,39 @@ internal void Apply(AkkaConfigurationBuilder builder)

var sb = new StringBuilder();

if (Role is { })
if (Role is not null)
sb.AppendLine($"role = {Role.ToHocon()}");

if(RememberEntities is { })
if(RememberEntities is not null)
sb.AppendLine($"remember-entities = {RememberEntities.ToHocon()}");

if(RememberEntitiesStore is { })
if(RememberEntitiesStore is not null)
sb.AppendLine($"remember-entities-store = {RememberEntitiesStore.ToString().ToLowerInvariant().ToHocon()}");

var journalId = JournalOptions?.PluginId ?? JournalPluginId ?? null;
if (journalId is { })
if (journalId is not null)
sb.AppendLine($"journal-plugin-id = {journalId.ToHocon()}");

var snapshotId = SnapshotOptions?.PluginId ?? SnapshotPluginId ?? null;
if (snapshotId is { })
if (snapshotId is not null)
sb.AppendLine($"snapshot-plugin-id = {snapshotId.ToHocon()}");

if (StateStoreMode is { })
if (StateStoreMode is not null)
sb.AppendLine($"state-store-mode = {StateStoreMode.ToString().ToLowerInvariant().ToHocon()}");

if (LeaseImplementation is { })
if (LeaseImplementation is not null)
sb.AppendLine($"use-lease = {LeaseImplementation.ConfigPath}");

if (LeaseRetryInterval is { })
if (LeaseRetryInterval is not null)
sb.AppendLine($"lease-retry-interval = {LeaseRetryInterval.ToHocon()}");

if (FailOnInvalidEntityStateTransition is { })
if (FailOnInvalidEntityStateTransition is not null)
sb.AppendLine(
$"fail-on-invalid-entity-state-transition = {FailOnInvalidEntityStateTransition.ToHocon()}");

if(ShouldPassivateIdleEntities is false)
sb.AppendLine("passivate-idle-entity-after = off");
else if(PassivateIdleEntityAfter is { })
else if(PassivateIdleEntityAfter is not null)
sb.AppendLine($"passivate-idle-entity-after = {PassivateIdleEntityAfter.ToHocon()}");

if (sb.Length > 0)
Expand All @@ -353,9 +362,9 @@ internal void Apply(AkkaConfigurationBuilder builder)
base.Apply(builder, "akka.cluster.sharding");

var sb = new StringBuilder();
if (MajorityMinimumCapacity is { })
if (MajorityMinimumCapacity is not null)
sb.AppendLine($"majority-min-cap = {MajorityMinimumCapacity}");
if (MaxDeltaElements is { })
if (MaxDeltaElements is not null)
sb.AppendLine($"max-delta-elements = {MaxDeltaElements}");

if(sb.Length == 0)
Expand Down Expand Up @@ -407,28 +416,28 @@ internal virtual void Apply(AkkaConfigurationBuilder builder, string prefix = "a
{
var sb = new StringBuilder();

if (Name is { })
if (Name is not null)
sb.AppendLine($"name = {Name.ToHocon()}");
if (Role is { })
if (Role is not null)
sb.AppendLine($"role = {Role.ToHocon()}");
if (RecreateOnFailure is { })
if (RecreateOnFailure is not null)
sb.AppendLine($"recreate-on-failure = {RecreateOnFailure.ToHocon()}");
if (PreferOldest is { })
if (PreferOldest is not null)
sb.AppendLine($"prefer-oldest = {PreferOldest.ToHocon()}");
if (VerboseDebugLogging is { })
if (VerboseDebugLogging is not null)
sb.AppendLine($"verbose-debug-logging = {VerboseDebugLogging.ToHocon()}");

var durableSb = new StringBuilder();
if (Durable.Keys is { })
if (Durable.Keys is not null)
durableSb.AppendLine($"keys = [{string.Join(",", Durable.Keys.Select(s => s.ToHocon()))}]");

var lmdbSb = new StringBuilder();
var lmdb = Durable.Lmdb;
if (lmdb.Directory is { })
if (lmdb.Directory is not null)
lmdbSb.AppendLine($"dir = {lmdb.Directory.ToHocon()}");
if (lmdb.MapSize is { })
if (lmdb.MapSize is not null)
lmdbSb.AppendLine($"map-size = {lmdb.MapSize}");
if (lmdb.WriteBehindInterval is { })
if (lmdb.WriteBehindInterval is not null)
lmdbSb.AppendLine($"write-behind-interval = {lmdb.WriteBehindInterval.ToHocon()}");

if (lmdbSb.Length > 0)
Expand Down Expand Up @@ -551,18 +560,28 @@ internal static AkkaConfigurationBuilder BuildClusterHocon(
sb.AppendLine("]");
}

if (options.MinimumNumberOfMembers is { })
if (options.MinimumNumberOfMembers is not null)
sb.AppendLine($"min-nr-of-members = {options.MinimumNumberOfMembers}");

if (options.AppVersion is { })
if (options.AppVersion is not null)
sb.AppendLine($"app-version = {options.AppVersion.ToHocon()}");

if (options.LogInfo is { })
if (options.LogInfo is not null)
sb.AppendLine($"log-info = {options.LogInfo.ToHocon()}");

if (options.LogInfoVerbose is { })
if (options.LogInfoVerbose is not null)
sb.AppendLine($"log-info-verbose = {options.LogInfoVerbose.ToHocon()}");

if (options.FailureDetector is not null)
{
var fsb = options.FailureDetector.ToHocon();
if (fsb.Length > 0)
{
sb.AppendLine("failure-detector {\n");
sb.Append(fsb);
sb.AppendLine("}");
}
}
sb.AppendLine("}");

// prepend the composed configuration
Expand Down Expand Up @@ -1080,7 +1099,7 @@ public static AkkaConfigurationBuilder WithSingleton<TKey>(
var clusterSingletonManagerSettings =
ClusterSingletonManagerSettings.Create(system).WithSingletonName(singletonName);
if (options.LeaseImplementation is { })
if (options.LeaseImplementation is not null)
{
var retry = options.LeaseRetryInterval ?? TimeSpan.FromSeconds(5);
clusterSingletonManagerSettings = clusterSingletonManagerSettings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ namespace Akka.Cluster.Hosting
{
public ClusterOptions() { }
public string? AppVersion { get; set; }
public Akka.Remote.Hosting.PhiAccrualFailureDetectorOptions? FailureDetector { get; set; }
public bool? LogInfo { get; set; }
public bool? LogInfoVerbose { get; set; }
public int? MinimumNumberOfMembers { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,14 @@ namespace Akka.Hosting
{
public static string ToHocon(this bool value) { }
public static string ToHocon(this bool? value) { }
public static string ToHocon(this double value) { }
public static string ToHocon(this double? value) { }
public static string ToHocon(this float value) { }
public static string ToHocon(this float? value) { }
public static string ToHocon(this int value) { }
public static string ToHocon(this int? value) { }
public static string ToHocon(this long value) { }
public static string ToHocon(this long? value) { }
public static string ToHocon(this string? text) { }
public static string ToHocon(this System.TimeSpan value, bool allowInfinite = false, bool zeroIsInfinite = false) { }
public static string ToHocon(this System.TimeSpan? value, bool allowInfinite = false, bool zeroIsInfinite = false) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,39 @@
public static Akka.Hosting.AkkaConfigurationBuilder WithRemoting(this Akka.Hosting.AkkaConfigurationBuilder builder, System.Action<Akka.Remote.Hosting.RemoteOptions> configure) { }
public static Akka.Hosting.AkkaConfigurationBuilder WithRemoting(this Akka.Hosting.AkkaConfigurationBuilder builder, string? hostname = null, int? port = default, string? publicHostname = null, int? publicPort = default) { }
}
public class DeadlineFailureDetectorOptions
{
public DeadlineFailureDetectorOptions() { }
public System.TimeSpan? AcceptableHeartbeatPause { get; set; }
public System.TimeSpan? HeartbeatInterval { get; set; }
public System.Text.StringBuilder ToHocon() { }
}
public class PhiAccrualFailureDetectorOptions
{
public PhiAccrualFailureDetectorOptions() { }
public System.TimeSpan? AcceptableHeartbeatPause { get; set; }
public System.TimeSpan? ExpectedResponseAfter { get; set; }
public System.TimeSpan? HeartbeatInterval { get; set; }
public int? MaxSampleSize { get; set; }
public System.TimeSpan? MinStandardDeviation { get; set; }
public double? Threshold { get; set; }
public System.TimeSpan? UnreachableNodesReaperInterval { get; set; }
public System.Text.StringBuilder ToHocon() { }
}
public class RemoteOptions
{
public RemoteOptions() { }
public bool? EnableSsl { get; set; }
public string? HostName { get; set; }
public long? MaxFrameSize { get; set; }
public int? Port { get; set; }
public string? PublicHostName { get; set; }
public int? PublicPort { get; set; }
public long? ReceiveBufferSize { get; set; }
public long? SendBufferSize { get; set; }
public Akka.Remote.Hosting.SslOptions Ssl { get; set; }
public Akka.Remote.Hosting.DeadlineFailureDetectorOptions? TransportFailureDetector { get; set; }
public Akka.Remote.Hosting.PhiAccrualFailureDetectorOptions? WatchFailureDetector { get; set; }
}
public sealed class SslCertificateOptions
{
Expand Down
48 changes: 48 additions & 0 deletions src/Akka.Hosting/HoconExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,53 @@ public static string ToHocon(this TimeSpan value, bool allowInfinite = false, bo

return value.TotalMilliseconds.ToString(CultureInfo.InvariantCulture);
}

public static string ToHocon(this float? value)
{
if(value is null)
throw new ConfigurationException("Value can not be null");
return ToHocon(value.Value);
}

public static string ToHocon(this float value)
{
return value.ToString(CultureInfo.InvariantCulture);
}

public static string ToHocon(this double? value)
{
if(value is null)
throw new ConfigurationException("Value can not be null");
return ToHocon(value.Value);
}

public static string ToHocon(this double value)
{
return value.ToString(CultureInfo.InvariantCulture);
}

public static string ToHocon(this int? value)
{
if(value is null)
throw new ConfigurationException("Value can not be null");
return ToHocon(value.Value);
}

public static string ToHocon(this int value)
{
return value.ToString(CultureInfo.InvariantCulture);
}

public static string ToHocon(this long? value)
{
if(value is null)
throw new ConfigurationException("Value can not be null");
return ToHocon(value.Value);
}

public static string ToHocon(this long value)
{
return value.ToString(CultureInfo.InvariantCulture);
}
}
}
Loading

0 comments on commit 6fd3472

Please sign in to comment.