Skip to content

Commit

Permalink
Add API for UntypedActorWithStash types (#6327)
Browse files Browse the repository at this point in the history
Co-authored-by: Aaron Stannard <aaron@petabridge.com>
  • Loading branch information
ismaelhamed and Aaronontheweb committed Jan 19, 2023
1 parent 1aff6c7 commit 303ad3c
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 12 deletions.
18 changes: 6 additions & 12 deletions docs/articles/actors/untyped-actor-api.md
Expand Up @@ -708,18 +708,16 @@ static void Main(string[] args)

## Stash

The `IWithStash` interface enables an actor to temporarily stash away messages that can not or should not be handled using the actor's current behavior. Upon changing the actor's message handler, i.e., right before invoking `Context.Become()` or `Context.Unbecome()`, all stashed messages can be "un-stashed", thereby prepending them to the actor's mailbox. This way, the stashed messages can be processed in the same order as they have been received originally.
The `UntypedActorWithStash` class enables an actor to temporarily stash away messages that can not or should not be handled using the actor's current behavior. Upon changing the actor's message handler, i.e., right before invoking `Context.Become()` or `Context.Unbecome()`, all stashed messages can be "un-stashed", thereby prepending them to the actor's mailbox. This way, the stashed messages can be processed in the same order as they have been received originally. An actor that extends `UntypedActorWithStash` will automatically get a deque-based mailbox.

> [!NOTE]
> The interface `IWithStash` implements the marker interface `IRequiresMessageQueue<IDequeBasedMessageQueueSemantics>` which requests the system to automatically choose a deque-based mailbox implementation for the actor (defaults to an unbounded deque mailbox). If you want more control over the mailbox, see the documentation on mailboxes: [Mailboxes](xref:mailboxes).
> The abstract class `UntypedActorWithStash` implements the marker interface `IRequiresMessageQueue<IDequeBasedMessageQueueSemantics>` which requests the system to automatically choose a deque-based mailbox implementation for the actor. If you want more control over the mailbox, see the documentation on mailboxes: [Mailboxes](xref:mailboxes).
Here is an example of the `IWithStash` interface in action:
Here is an example of the `UntypedActorWithStash` interface in action:

```csharp
public class ActorWithProtocol : UntypedActor, IWithStash
public class ActorWithProtocol : UntypedActorWithStash
{
public IStash Stash { get; set; }

protected override void OnReceive(object message)
{
switch (message)
Expand Down Expand Up @@ -755,14 +753,10 @@ Invoking `Stash()` adds the current message (the message that the actor received

Invoking `UnstashAll()` enqueues messages from the stash to the actor's mailbox until the capacity of the mailbox (if any) has been reached (note that messages from the stash are prepended to the mailbox). In case a bounded mailbox overflows, a `MessageQueueAppendFailedException` is thrown. The stash is guaranteed to be empty after calling `UnstashAll()`.

Note that the stash is part of the ephemeral actor state, unlike the mailbox. Therefore, it should be managed like other parts of the actor's state which have the same property.

However, the `IWithStash` interface implementation of `PreRestart` will call `UnstashAll()`. This means that before the actor restarts, it will transfer all stashed messages back to the actor’s mailbox.

The result of this is that when an actor is restarted, any stashed messages will be delivered to the new incarnation of the actor. This is usually the desired behavior.
Note that the stash is part of the ephemeral actor state, unlike the mailbox. Therefore, it should be managed like other parts of the actor's state which have the same property. The `UntypedActorWithStash` implementation of `PreRestart` will call `UnstashAll()`, which is usually the desired behavior.

> [!NOTE]
> If you want to enforce that your actor can only work with an unbounded stash, then you should use the `IWithUnboundedStash` interface instead.
> If you want to enforce that your actor can only work with an unbounded stash, then you should use the `UntypedActorWithUnboundedStash` class instead.
## Killing an Actor

Expand Down
Expand Up @@ -1865,6 +1865,21 @@ namespace Akka.Actor
protected void RunTask(System.Action action) { }
protected void RunTask(System.Func<System.Threading.Tasks.Task> action) { }
}
public abstract class UntypedActorWithStash : Akka.Actor.UntypedActor, Akka.Actor.IActorStash, Akka.Actor.IWithStash, Akka.Actor.IWithUnrestrictedStash, Akka.Dispatch.IRequiresMessageQueue<Akka.Dispatch.IDequeBasedMessageQueueSemantics>
{
protected UntypedActorWithStash() { }
public Akka.Actor.IStash Stash { get; set; }
}
public abstract class UntypedActorWithUnboundedStash : Akka.Actor.UntypedActor, Akka.Actor.IActorStash, Akka.Actor.IWithUnboundedStash, Akka.Actor.IWithUnrestrictedStash, Akka.Dispatch.IRequiresMessageQueue<Akka.Dispatch.IUnboundedDequeBasedMessageQueueSemantics>
{
protected UntypedActorWithUnboundedStash() { }
public Akka.Actor.IStash Stash { get; set; }
}
public abstract class UntypedActorWithUnrestrictedStash : Akka.Actor.UntypedActor, Akka.Actor.IActorStash, Akka.Actor.IWithUnrestrictedStash
{
protected UntypedActorWithUnrestrictedStash() { }
public Akka.Actor.IStash Stash { get; set; }
}
public delegate void UntypedReceive(object message);
public class static WrappedMessage
{
Expand Down
Expand Up @@ -1867,6 +1867,21 @@ namespace Akka.Actor
protected void RunTask(System.Action action) { }
protected void RunTask(System.Func<System.Threading.Tasks.Task> action) { }
}
public abstract class UntypedActorWithStash : Akka.Actor.UntypedActor, Akka.Actor.IActorStash, Akka.Actor.IWithStash, Akka.Actor.IWithUnrestrictedStash, Akka.Dispatch.IRequiresMessageQueue<Akka.Dispatch.IDequeBasedMessageQueueSemantics>
{
protected UntypedActorWithStash() { }
public Akka.Actor.IStash Stash { get; set; }
}
public abstract class UntypedActorWithUnboundedStash : Akka.Actor.UntypedActor, Akka.Actor.IActorStash, Akka.Actor.IWithUnboundedStash, Akka.Actor.IWithUnrestrictedStash, Akka.Dispatch.IRequiresMessageQueue<Akka.Dispatch.IUnboundedDequeBasedMessageQueueSemantics>
{
protected UntypedActorWithUnboundedStash() { }
public Akka.Actor.IStash Stash { get; set; }
}
public abstract class UntypedActorWithUnrestrictedStash : Akka.Actor.UntypedActor, Akka.Actor.IActorStash, Akka.Actor.IWithUnrestrictedStash
{
protected UntypedActorWithUnrestrictedStash() { }
public Akka.Actor.IStash Stash { get; set; }
}
public delegate void UntypedReceive(object message);
public class static WrappedMessage
{
Expand Down
Expand Up @@ -1865,6 +1865,21 @@ namespace Akka.Actor
protected void RunTask(System.Action action) { }
protected void RunTask(System.Func<System.Threading.Tasks.Task> action) { }
}
public abstract class UntypedActorWithStash : Akka.Actor.UntypedActor, Akka.Actor.IActorStash, Akka.Actor.IWithStash, Akka.Actor.IWithUnrestrictedStash, Akka.Dispatch.IRequiresMessageQueue<Akka.Dispatch.IDequeBasedMessageQueueSemantics>
{
protected UntypedActorWithStash() { }
public Akka.Actor.IStash Stash { get; set; }
}
public abstract class UntypedActorWithUnboundedStash : Akka.Actor.UntypedActor, Akka.Actor.IActorStash, Akka.Actor.IWithUnboundedStash, Akka.Actor.IWithUnrestrictedStash, Akka.Dispatch.IRequiresMessageQueue<Akka.Dispatch.IUnboundedDequeBasedMessageQueueSemantics>
{
protected UntypedActorWithUnboundedStash() { }
public Akka.Actor.IStash Stash { get; set; }
}
public abstract class UntypedActorWithUnrestrictedStash : Akka.Actor.UntypedActor, Akka.Actor.IActorStash, Akka.Actor.IWithUnrestrictedStash
{
protected UntypedActorWithUnrestrictedStash() { }
public Akka.Actor.IStash Stash { get; set; }
}
public delegate void UntypedReceive(object message);
public class static WrappedMessage
{
Expand Down
59 changes: 59 additions & 0 deletions src/core/Akka/Actor/UntypedActorWithStash.cs
@@ -0,0 +1,59 @@
//-----------------------------------------------------------------------
// <copyright file="UntypedActorWithStash.cs" company="Akka.NET Project">
// Copyright (C) 2009-2022 Lightbend Inc. <http://www.lightbend.com>
// Copyright (C) 2013-2022 .NET Foundation <https://github.com/akkadotnet/akka.net>
// </copyright>
//-----------------------------------------------------------------------

using Akka.Dispatch;

namespace Akka.Actor
{
/// <summary>
/// Actor base class that should be extended to create an actor with a stash.
/// <para>
/// The stash enables an actor to temporarily stash away messages that can not or
/// should not be handled using the actor's current behavior.
/// </para>
/// <para>
/// Note that the subclasses of `UntypedActorWithStash` by default request a Deque based mailbox since this class
/// implements the <see cref="IRequiresMessageQueue{T}"/> marker interface.
/// </para>
/// You can override the default mailbox provided when `IDequeBasedMessageQueueSemantics` are requested via config:
/// <code>
/// akka.actor.mailbox.requirements {
/// "Akka.Dispatch.IDequeBasedMessageQueueSemantics" = your-custom-mailbox
/// }
/// </code>
/// Alternatively, you can add your own requirement marker to the actor and configure a mailbox type to be used
/// for your marker.
/// <para>
/// For a `Stash` based actor that enforces unbounded deques see <see cref="UntypedActorWithUnboundedStash"/>.
/// There is also an unrestricted version <see cref="UntypedActorWithUnrestrictedStash"/> that does not
/// enforce the mailbox type.
/// </para>
/// </summary>
public abstract class UntypedActorWithStash : UntypedActor, IWithStash
{
public IStash Stash { get; set; }
}

/// <summary>
/// Actor base class with `Stash` that enforces an unbounded deque for the actor.
/// See <see cref="UntypedActorWithStash"/> for details on how `Stash` works.
/// </summary>
public abstract class UntypedActorWithUnboundedStash : UntypedActor, IWithUnboundedStash
{
public IStash Stash { get; set; }
}

/// <summary>
/// Actor base class with `Stash` that does not enforce any mailbox type. The proper mailbox has to be configured
/// manually, and the mailbox should extend the <see cref="IDequeBasedMessageQueueSemantics"/> marker interface.
/// See <see cref="UntypedActorWithStash"/> for details on how `Stash` works.
/// </summary>
public abstract class UntypedActorWithUnrestrictedStash : UntypedActor, IWithUnrestrictedStash
{
public IStash Stash { get; set; }
}
}

0 comments on commit 303ad3c

Please sign in to comment.