How to handle a stale akka.net remote iactorref - akka.net

I am using remote actors in a akka.net in a windows service that stays up long term. I obtain an IActorRef for the remote actor using ActorSelection and I keep this IActorRef alive for an extended period of time in the service. The IActorRef points to an actor system running in another windows service.
I understand that a restart of the remote actor will not invalidate the remote actor ref. However, it is conceivable that the remote windows service might get restarted at some point and the IActorRef in the calling windows service would become invalid.
What is the best practice for handling this?
A naive approach would be to use ActorSelection to obtain a new IActorRef every time I want to make a call to the remote actor. This would obviously be inefficient.
Another approach might be to simply wrap every call I make on that IActorRef in some kind of error handling envelope that traps exceptions and obtains a new IActorRef using actorselection and retries? Or the envelope might make a test call before every actual call to see if the remote actor is still alive and if not get a new actor ref.
Any better way?

Default option for detecting dead actors is to Watch them (see documentation). When one actor watches another, it will receive Terminated message, once watched actor becomes dead or unreachable.

Watching for a Terminated message will alert a system that a remote actor has died, but there is a question of how exactly to respond to a terminated remote actor. Assuming that an actor obtains an IActorRef to a remote actor via its constructor, how would the actor obtain a new IActorRef to the remote actor when it becomes alive again. One way would be to have the actor fail and delegate to parent actor which would then obtain a new IActorRef to the remote actor via actor selection. The problem with this, however, is that the original actor selection for the remote actor might have taken place in non-actor code in a composition root where dependency injection normally would occur. I suppose you could get around this by passing a actor selection factory delegate around which could be used to reconstruct the remote IActorRef. An alternative that I came up with is to create a wrapper class that implements IActorRef called FaultTolerantActorRef.
This class takes the path of the remote (or local) actor in the constructor and periodically does an actor selection to get a refresh IActorRef to the remote actor. This way if for some reason the remote actor dies calls on the FaultTolerantActorRef will end up in dead letters while the remote actor is dead. However, when the remote actor eventually comes on line again, calls to the FaultTolerantActorRef will eventually reach the newly revived remote actor without having to take any explicit action on the part of the calling local actor.
There is an Invalidate method which will force the FaultTolerantActorRef to do a fresh actor selection on the next call. This could presumebly be called by an actor in response to a Terminated message from the remote actor. Even without calling Invalidate, a fresh actor selection will occur based on the refresh interval passed to the constructor.
using Akka.Actor;
using System;
using Akka.Util;
using System.Threading;
namespace JA.AkkaCore
{
public class FaultTolerantActorRef : IActorRef
{
public IActorRef ActorRef
{
get
{
if (!_valid || DateTime.Now.Ticks > Interlocked.Read(ref _nextRefreshTime))
RefreshActorRef();
return _actorRef;
}
}
public ActorPath Path
{
get
{
return ActorRef.Path;
}
}
object _lock = new object();
IActorRef _actorRef;
volatile bool _valid;
string _path;
IActorRefFactory _actorSystem;
private TimeSpan _requestTimeout;
private TimeSpan _refreshInterval;
//private DateTime _nextRefreshTime = DateTime.MinValue;
private long _nextRefreshTime = DateTime.MinValue.Ticks;
public FaultTolerantActorRef(IActorRefFactory actorSystem, IActorRef actorRef,
TimeSpan refreshInterval = default(TimeSpan), TimeSpan requestTimeout = default(TimeSpan))
: this(actorSystem, actorRef.Path.ToString(), refreshInterval, requestTimeout)
{
_actorRef = actorRef;
_valid = true;
}
public FaultTolerantActorRef(IActorRefFactory actorSystem, string actorPath,
TimeSpan refreshInterval = default(TimeSpan), TimeSpan requestTimeout = default(TimeSpan))
{
if (refreshInterval == default(TimeSpan))
_refreshInterval = TimeSpan.FromSeconds(60);
else
_refreshInterval = refreshInterval;
if (requestTimeout == default(TimeSpan))
_requestTimeout = TimeSpan.FromSeconds(60);
else
_requestTimeout = requestTimeout;
_actorSystem = actorSystem;
_valid = false;
_path = actorPath;
}
private void RefreshActorRef()
{
lock(_lock)
{
if (!_valid || DateTime.Now.Ticks > _nextRefreshTime)
{
_actorRef = _actorSystem.ActorSelectionOne(_path, _requestTimeout);
Interlocked.Exchange(ref _nextRefreshTime,DateTime.Now.Ticks + _refreshInterval.Ticks);
_valid = true;
}
}
}
public void Invalidate()
{
_valid = false;
}
public void Tell(object message, IActorRef sender)
{
ActorRef.Tell(message, sender);
}
public bool Equals(IActorRef other)
{
return ActorRef.Equals(other);
}
public int CompareTo(IActorRef other)
{
return ActorRef.CompareTo(other);
}
public ISurrogate ToSurrogate(ActorSystem system)
{
return ActorRef.ToSurrogate(system);
}
public int CompareTo(object obj)
{
return ActorRef.CompareTo(obj);
}
}
}

Related

Handling a received not covered in become

I am using Akka.NET to develop a logistics simulation.
Having tried various patterns, it seems to me that FSM-type behaviour using become will substantially simplify development.
The system has a repeating clock tick message that all relevant actors receive in order to simulate accelerated passage of time for the entire simulation system. This clock tick message should be handled by all actors that are subscribed to it regardless of which message loop is currently active for any specific actor.
Am I correct in thinking that the only way to handle the clock message in all message loops is by explicitly checking for it in all message loops, or is there a way of defining messages that are handled regardless of which message loop is active?
If the former is the case my idea is to check for a clock tick message in a ReceiveAny, which all the message loops need to have anyway, and to then pass it on to an appropriate handler.
You could use Stashing to Stash the messages while Simulating. I came up with the following code sample to better explain how that works:
// See https://aka.ms/new-console-template for more information
using Akka.Actor;
using Akka.NET_StackOverflow_Questions_tryout.Questions;
var actorSystem = ActorSystem.Create("stackOverFlow");
var sim = actorSystem.ActorOf(Props.Create(()=> new StackOverflow71079733()));
sim.Tell(5000L);
sim.Tell("string");
sim.Tell(1000L);
sim.Tell("strin2");
sim.Tell("strin3");
Console.ReadLine();
public class StackOverflow71079733 : ReceiveActor, IWithUnboundedStash
{
public IStash Stash { get ; set ; }
private readonly IActorRef _simActor;
public StackOverflow71079733()
{
_simActor = Context.ActorOf<SimulationActor>();
ClockTickMessage();
}
private void Simulate(long ticks)
{
Console.WriteLine($"Ticks: {ticks}");
Receive<Done>(d =>
{
Console.WriteLine("Simulation done");
Become(ClockTickMessage);
Stash?.Unstash();
});
// you can add additional messages that may to be handled while the simulation is happening
// e.g:
Receive<string>(s => Console.WriteLine($"received in '{s}' in simulation"));
//While the simulation is on-going, add the incoming message into a queue/stash it
// so that it is not lost and can be picked and handled after stimulation is done
ReceiveAny(any =>
{
Stash.Stash();
Console.WriteLine($"Stashed Ticks: {any}");
});
_simActor.Tell(ticks);
}
private void ClockTickMessage()
{
// you can create an object to represent the ClockTickMessage
Receive<long>(ticks =>
{
Become(() => Simulate(ticks));
});
}
}
/// <summary>
/// We need to run simulation in a another actor so that the parent actor can keep receiving ClockTicksMessages
/// In case the sim takes a long time to become
/// </summary>
public sealed class SimulationActor : ReceiveActor
{
private IActorRef _sender;
public SimulationActor()
{
Receive<long>(l =>
{
_sender = Sender;
Thread.Sleep(TimeSpan.FromMilliseconds(l));
_sender.Tell(Done.Instance);
});
}
}
public sealed class Done
{
public static Done Instance = new Done();
}

Akka.Net switching behavior with Become()

I have built as simple actor which accepts two messages: TicketValidated and BarrierPush but the switching is not happening as intended.
public class TurnstileActor : ReceiveActor
{
public TurnstileActor()
{
Become(Locked);
}
public void Locked()
{
Receive<TicketValidated>(msg => Become(Unlocked));
Receive<BarrierPush>(msg => { Console.WriteLine("Locked");});
}
public void Unlocked()
{
Receive<TicketValidated>(msg =>
Console.WriteLine("Unlocked"));
Receive<BarrierPush>(msg => Become(Locked));
}
}
Main class
var system = ActorSystem.Create("ActorSystem");
var actor = system.ActorOf<TurnstileActor>("actor");
actor.Tell(new TicketValidated());
Actual execution is: the Locked() method is called from the constructor and TicketValidated message is received. Become(Unlocked) is executed correctly and it enters Unlocked() method but then Console.WriteLine("Unlocked") is not called.
Could the Akka.Net library be broken?
To understand this behaviour, consider what happens when Become(Unlocked) is executed, and it enters the Unlocked() method. The Unlocked method in turn invokes the Receive method twice: These 2 calls to Receive register the new behaviour of this actor, affecting subsequent messages sent to this actor instance. The lambdas passed in to the Receive methods are not executed at this time - they represent the new behaviour that is registered, and that will be seen when subsequent messages are received.
That explains why "Unlocked" is not written to the console when "Become(Unlocked)" is executed - It will only be seen if the next message received is another "TicketValidated".

How to create actor with parameterized constructor from testprobe

I am trying test to MyActor for sending a MessageB to itself on condition. MyActor takes setting as constructor parameter. Setting doesn't have setter cause it is intended to be immutable after creation.
public class MyActor : ReceiveActor
{
private bool Setting { get; }
public MyActor(bool setting)
{
Setting = setting;
Receive<MessageA>(message => HandleMessageA(message));
}
public void HandleMessageA(MessageA message)
{
if (Setting)
Self.Tell(new MessageB);
}
}
And here is the test
[Test]
public void HandleMessageA_SettingIsTrue_MessageBIsSent()
{
bool setting = true;
var testProbe = this.CreateTestProbe();
var myActor = Props.Create<MyActor>(testProbbe);
myActor.Tell(new MessageA);
myActor.ExpectMsg<MessageB>();
}
My problem is that i don't know how to pass bool setting to constructor.
Well I can write it like this
bool setting = true;
var myActor = Props.Create<MyActor>(setting);
And this way myActor will have settings set. But than I didn't use TestProbe and therefore will not be able to listen for expected message. So my question is how make Arrange section of test correctly?
A great guide to testing with Akka.NET describes how to create actors within the test system:
Create your actors within Sys so that your actors exist in the same
ActorSystem as the TestActor.
// create an actor in the TestActorSystem
var actor = Sys.ActorOf(Props.Create(() => new MyActorClass()));
Well the situation you have created is rather artificial. Because in a real world scenario you would either send MessageB to another actor. Which you would then be able to substitute with a TestProbe. Or you would verify the sideeffect that your messageB would have. So for example sending messageB to Self, would update some property on your actor, which you could then Test for.
Also see Chima's response, he shows the correct way to create your actor. Because only instantiating the Props is not enough.
And some general advice. When testing actors, you will want to try to refrain from testing for individual messages. Try and test the outcome (or side-effect) of sending those messages instead. That way your tests are a lot less brittle should you ever refactor your Actor's interactions

Async End<Method> not called in WCF

I have the following situation: My WCF service allows a client to register to wait for some event. The waiting is asynchronous on the service side, that is, the waiter is registered and when the process is finished, the waiter is notified. At the moment, it's simply a ManualResetEvent.
Now I want to expose this method via WCF. I tried to use AsyncPattern=true and created two methods, BeginWait which bundles the event into a IAsyncResult, and EndWait which calls AsyncWaitHandle.WaitOne(). However, if I call BeginWait, EndWait from the client, the server side EndWait is not executed. I'm using a manually implemented wrapper (my proxy class is derived from ChannelBase<IWaitService>, IWaitService), which basically calls Channel.EndWait(), and this function is indeed called; but on the server side, the call never arrives.
What am I doing wrong here? Follow-up question: If the asynchronous call is working, is there an easy way to make that synchronous on the client side?
The line
var task = Task.Factory.StartNew(() => IsPrime(a));
uses overload
TaskFactory.StartNew(Action)
which results in
((IAsyncResult)task).AsyncState == null
the call to callback(task) results in an ArgumentException, complaining that the state object is different from the state object that was passed to the BeginXxx method. The line must be modified to
var task = Task.Factory.StartNew((actionState) => IsPrime(a), state);
using overload
TaskFactory.StartNew(Action<object>, object)
such that the state object passed by WCF ends up in the task:
((IAsyncResult)task).AsyncState.GetType().FullName == System.ServiceModel.Dispatcher.MessageRpc+Wrapper
The synchronous/asynchronous decision can be made independently on the server or client side. Calling EndWait on the client does not translate into an EndWait call on the server. I would recommend testing an async service with a sync client just to keep things simple and avoid confusion.
I would further recommend that you not call WaitOne inside of the EndWait method. The contract is that this method will only be called after the IAsyncResult tells the framework that it is done. This is done in one of three ways:
CompletedSynchronously returns true
The AsyncCallback is invoked
The AsyncWaitHandle is signalled
CompletedSynchronously should only return true if BeginWait had enough information to complete the request before it returned. This is probably not the case. You can satisfy the other two conditions with your ManualResetEvent as follows:
class EventBasedAsyncResult : IAsyncResult
{
private readonly ManualResetEvent _manualResetEvent;
private readonly AsyncCallback _asyncCallback;
private readonly object _asyncState;
public EventBasedAsyncResult(AsyncCallback callback, object asyncState)
{
_manualResetEvent = new ManualResetEvent(false);
_asyncState = asyncState;
_asyncCallback = callback;
}
public void WaitCompleted()
{
_manualResetEvent.Set();
_asyncCallback(this);
}
public object AsyncState
{
get { return _asyncState; }
}
public WaitHandle AsyncWaitHandle
{
get { return _manualResetEvent; }
}
public bool CompletedSynchronously
{
get { return false; }
}
public bool IsCompleted
{
get { return _manualResetEvent.WaitOne(0); }
}
}
I think once you do this you'll find that EndWait is called, even if the client is synchronous.

WCF Callback channel faulted

I'm trying to implement a reconnect logic for a wcf client. I'm aware that you have to create a new channel after the current channel entered the faulted state. I did this in a channel faulted event handler:
internal class ServiceClient : DuplexClientBase, IServiceClient
{
public ServiceClient(ICallback callback, EndpointAddress serviceAddress)
: base(callback, MyUtility.GetServiceBinding("NetTcpBinding"), serviceAddress)
{
// Open the connection.
Open();
}
public void Register(string clientName)
{
// register to service
}
public void DoSomething()
{
// some code
}
}
public class ClientApp
{
private IServiceClient mServiceClient;
private ICallback mCallback;
public ClientApp()
{
mServiceClient = new ServiceClient( mCallback, new EndpointAddress("someAddress"));
mServiceClient.Register();
// register faulted event for the service client
((ICommunicationObject)mServiceClient).Faulted += new EventHandler(ServiceClient_Faulted);
}
void ServiceClient_Faulted(object sender, EventArgs e)
{
// Create new Service Client.
mServiceClient = new ServiceClient( mCallback, new EndpointAddress("someAddress"));
// Register the EI at Cell Controller
mServiceClient.Register();
}
public void DoSomething()
{
mServiceClient.DoSomething();
}
}
But in my unit test I still get a "The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state" exception.
Is it possible that the callback channel is still faulted and if yes how can I replace the callback channel?
so far I have experienced that a WCF connection needs to be recreated on fault - there doesn't seem to be a way to recover it otherwise. As for when a fault occurs, the method seems to fire fine, but often it fires and cleans up the WCF connection (establishing a new one, etc) as the current request is going through - causing this to fail - especially true on timeouts.
A couple of suggestions:
- If it is timeout related, keep track of the last time a call was made and a constant containing the timeout value. If the WCF connection will have been dropped due to inactivity, drop it and recreate it before you send the request over the wire.
- The other thing, it looks like you are not re-adding the fault handler, which means the first fault will get handled, but the second time it faults it will fall over without a handler cause no new one has been attached.
Hope this helps
Have you tried to reset the communications channel by calling mServiceClient.Abort in the Faulted event handler?
Edit:
I see that you do not reinitialize the mCallback object in your recovery code. You may need to assign it to a new instance.