Skip to content

Commit

Permalink
add GetAsync method to ActorRegistry (#33)
Browse files Browse the repository at this point in the history
* add `GetAsync` method to `ActorRegistry`

Enables entries to be added to the `ActorRegistry` later in the future (i.e. actors not started explicitly via Akka.Hosting's delegates - child actors typically) without forcing consumers of the registry to poll for changes.

* cleaned up cancellation

* fixed issues with ActorRegistry and `GetAsync`

* fixed duplicate registry entry detection

* added API approvals
  • Loading branch information
Aaronontheweb authored Jan 31, 2023
1 parent bfd72cd commit f34d888
Show file tree
Hide file tree
Showing 4 changed files with 248 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ namespace Akka.Hosting
{
public ActorRegistry() { }
public Akka.Actor.IActorRef Get<TKey>() { }
public System.Threading.Tasks.Task<Akka.Actor.IActorRef> GetAsync(System.Type key, System.Threading.CancellationToken ct = default) { }
public System.Threading.Tasks.Task<Akka.Actor.IActorRef> GetAsync<TKey>(System.Threading.CancellationToken ct = default) { }
public System.Collections.Generic.IEnumerator<System.Collections.Generic.KeyValuePair<System.Type, Akka.Actor.IActorRef>> GetEnumerator() { }
public void Register<TKey>(Akka.Actor.IActorRef actor, bool overwrite = false) { }
public bool TryGet(System.Type key, out Akka.Actor.IActorRef actor) { }
Expand All @@ -18,7 +20,7 @@ namespace Akka.Hosting
public ActorRegistryException(string message) { }
public ActorRegistryException(string message, System.Exception innerException) { }
}
public class ActorRegistryExtension : Akka.Actor.ExtensionIdProvider<Akka.Hosting.ActorRegistry>
public sealed class ActorRegistryExtension : Akka.Actor.ExtensionIdProvider<Akka.Hosting.ActorRegistry>
{
public ActorRegistryExtension() { }
public override Akka.Hosting.ActorRegistry CreateExtension(Akka.Actor.ExtendedActorSystem system) { }
Expand Down Expand Up @@ -114,6 +116,8 @@ namespace Akka.Hosting
public interface IReadOnlyActorRegistry : System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<System.Type, Akka.Actor.IActorRef>>, System.Collections.IEnumerable
{
Akka.Actor.IActorRef Get<TKey>();
System.Threading.Tasks.Task<Akka.Actor.IActorRef> GetAsync(System.Type key, System.Threading.CancellationToken ct = default);
System.Threading.Tasks.Task<Akka.Actor.IActorRef> GetAsync<TKey>(System.Threading.CancellationToken ct = default);
bool TryGet(System.Type key, out Akka.Actor.IActorRef actor);
bool TryGet<TKey>(out Akka.Actor.IActorRef actor);
}
Expand Down
76 changes: 76 additions & 0 deletions src/Akka.Hosting.Tests/ActorRegistrySpecs.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Akka.Actor;
using FluentAssertions;
using Xunit;
Expand Down Expand Up @@ -70,4 +73,77 @@ public void Should_not_throw_on_missing_entry_during_TryGet()
// assert
registry.Invoking(x => x.TryGet<Nobody>(out var actor)).Should().NotThrow<MissingActorRegistryEntryException>();
}

[Fact]
public async Task Should_complete_GetAsync_upon_KeyRegistered()
{
// arrange
var registry = new ActorRegistry();

// act
var task = registry.GetAsync<Nobody>();
task.IsCompletedSuccessfully.Should().BeFalse();

registry.Register<Nobody>(Nobody.Instance);
var result = await task;

// assert
result.Should().Be(Nobody.Instance);
}

[Fact]
public async Task Should_complete_multiple_GetAsync_upon_KeyRegistered()
{
// arrange
var registry = new ActorRegistry();

// act
var task1 = registry.GetAsync<Nobody>();
var task2 = registry.GetAsync<Nobody>();
var task3 = registry.GetAsync<Nobody>();

// validate that all three tasks are distinct
task1.Should().NotBe(task2).And.NotBe(task3);

var aggregate = Task.WhenAll(task1, task2, task3);

registry.Register<Nobody>(Nobody.Instance);
var result = await aggregate;

// assert
result.First().Should().Be(Nobody.Instance);
}

[Fact]
public void GetAsync_should_return_CompletedTask_if_Key_AlreadyExists()
{
// arrange
var registry = new ActorRegistry();
registry.Register<Nobody>(Nobody.Instance);

// act
var task = registry.GetAsync<Nobody>();

// assert
task.IsCompletedSuccessfully.Should().BeTrue();
}

[Fact]
public void GetAsync_should_Cancel_after_Timeout()
{
// arrange
var registry = new ActorRegistry();
var cancellationTokenSource = new CancellationTokenSource();

// act
var task = registry.GetAsync<Nobody>(cancellationTokenSource.Token);
Action cancel = () =>
{
cancellationTokenSource.Cancel();
task.Wait(TimeSpan.FromSeconds(3));
};

// assert
cancel.Should().Throw<TaskCanceledException>();
}
}
Loading

0 comments on commit f34d888

Please sign in to comment.