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".
Related
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();
}
I am attempting to get the address out of the callback function. I have been reading the documentation for CallBacks and some posts but still don't get why this is not working, as at the moment of returning the 'address' variable the callback has already finished.
private fun getAddressForCoordinates(geoCoordinates: GeoCoordinates):String {
address = "unchanged"
val maxItems = 1
val reverseGeocodingOptions = SearchOptions(LanguageCode.EN_GB, maxItems)
searchEngine.search(geoCoordinates, reverseGeocodingOptions, addressSearchCallback)
return address
}
private val addressSearchCallback =
SearchCallback { searchError, list ->
if (searchError != null) {
//showDialog("Reverse geocoding", "Error: $searchError")
Toast.makeText(context, "Error: $searchError", Toast.LENGTH_LONG).show()
return#SearchCallback
}
Toast.makeText(
context,
"Reverse geocoded address:" + list!![0].address.addressText,
Toast.LENGTH_LONG
).show()
address = list[0].address.addressText
}
From your code and comment I assume you are not familiar with the concept of asynchronous execution. That concept was well described here. I'll quote the main point:
When you execute something synchronously, you wait for it to finish
before moving on to another task. When you execute something
asynchronously, you can move on to another task before it finishes.
The fact that search() requires providing a callback and it doesn't simply return search results, is a good indication that it is most probably asynchronous. Invoking it is like saying: "Search for the data in the background and let me know when you have it. This is my email address - please send me my results there". Where email address is your callback. Invoking search() method does not block execution of your code, it does not wait for results - it only schedules searching and returns almost immediately.
Asynchronous processing is usually more tricky than a regular, synchronous code, but in many cases it is more efficient. In your case you can either try to "convert" original async API of the library to sync API that your code expects - but this is not recommended approach. Or you can redesign your code, so it will work asynchronously. For example, instead of doing this:
fun yourMethodThatNeedsAddress() {
val address = getAddressForCoordinates()
doSomethingWithAddress(address)
}
You need to do this:
fun yourMethodThatNeedsAddress() {
scheduleGetAddressForCoordinates() // renamed getAddressForCoordinates()
}
fun addressSearchCallback() {
...
doSomethingWithAddress(address)
}
So, whatever you planned to do with the acquired address, you can't do this straight after you started searching. You need to wait for a callback and then continue with processing of your address from there.
The SearchEngine from the 4.x HERE SDK needs an online connection as it is fetching results from a remote backend. This may take a few milliseconds, depending on your network connection. So, whenever you perform a search request, you need to wait until the callback is called:
searchEngine.search(geoCoordinates, reverseGeocodingOptions, addressSearchCallback)
When you call this, you pass addressSearchCallback as parameter. The implementation for addressSearchCallback can look like in your example. It will be called whenever the operation has finished. If the device is offline, then an error will be shown.
Note that the search() method is not returning any results immediately. These are passed to the callback, which happens asynchronously on a background thread. Thus, your application can continue to work without blocking any UI.
Once results are retrieved, the callback will be executed by the HERE SDK on the main thread.
So, if your code needs to do something with the address result, you have to do it inside the onSearchCompleted() method defined by the SearchCallback. If you write it in plain Java without lambda notation, it is more visible: You create a new SearchCallback object and pass it as parameter to the SearchEngine. The SearchEngine stores the object and executes the object's onSearchCompleted() whenever it thinks it's the right time:
private SearchCallback addressSearchCallback = new SearchCallback() {
#Override
public void onSearchCompleted(#Nullable SearchError searchError, #Nullable List<Place> list) {
if (searchError != null) {
showDialog("Reverse geocoding", "Error: " + searchError.toString());
return;
}
// If error is null, list is guaranteed to be not empty.
showDialog("Reverse geocoded address:", list.get(0).getAddress().addressText);
// Here is the place to do something more useful with the Address object ...!
}
};
I took this from this GitHub code snippet. Note that there is also an OfflineSearchEngine, that works without an internet connection, but for some reason it follows the same pattern and executes the task asynchronously.
private void getAddressForCoordinates(GeoCoordinates geoCoordinates) {
int maxItems = 1;
SearchOptions reverseGeocodingOptions = new SearchOptions(LanguageCode.EN_GB, maxItems);
searchEngine.search(geoCoordinates, reverseGeocodingOptions, new SearchCallback() {
#Override
public void onSearchCompleted(#Nullable SearchError searchError, #Nullable List<Place> list) {
if (searchError != null) {
showDialog("Reverse geocoding", "Error: " + searchError.toString());
return;
}
// If error is null, list is guaranteed to be not empty.
showDialog("Reverse geocoded address:", list.get(0).getAddress().addressText);
}
});
}
SearchEngine, a SearchOptions instance needs to be provided to set the desired LanguageCode. It determines the language of the resulting address. Then we can make a call to the engine's search()-method to search online for the address of the passed coordinates. In case of errors, such as when the device is offline, SearchError holds the error cause.
The reverse geocoding response contains either an error or a result: SearchError and the result list can never be null at the same time - or non-null at the same time.
The Address object contained inside each Place instance is a data class that contains multiple String fields describing the address of the raw location, such as country, city, street name, and many more. Consult the API Reference for more details. If you are only interested in receiving a readable address representation, you can access addressText, as shown in the above example. This is a String containing the most relevant address details, including the place's title.
Please refer to following link for detailed documentation on search() function and parameters associated with it.
https://developer.here.com/documentation/android-sdk-explore/4.4.0.2/dev_guide/topics/search.html
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
I'm using nServiceBus 5 and have created a number of host endpoints, two of which listen for database changes. (The specifics of how to do this can be found here). The intention is to have a service running in the background which publishes an event message using the Bus when notified to do so by the database listener.
The code which creates the database listener object and handles events is in the Start method, implemented as part of IWantToRunWhenBusStartsAndStops.
So - Is putting the code here likely to cause problems later on, for example if an exception is thrown (yes, I do have try/catch blocks, but I removed them from the sample code for clarity)? What happens when the Start method finishes executing?
Would I be better off with a constructor on my RequestNewQuoteSender class to instantiate the database listener as a class property and not use the Start method at all?
namespace MySample.QuoteRequest
{
public partial class RequestNewQuoteSender : IWantToRunWhenBusStartsAndStops
{
public void Start()
{
var changeListener = new DatabaseChangeListener(_ConnectionString);
// Assign the code within the braces to the DBListener's onChange event
changeListener.OnChange += () =>
{
// code to handle database change event
changeListener.Start(_SQLStatement);
};
// Now everything has been set up.... start it running.
changeListener.Start(_SQLStatement);
}
public void Stop() { LogInfo("Service Bus has stopped"); }
}
}
Your code seems fine to me.
Just a few small things:
Make changeListener a class field, so that it won't be GC (not 100% sure if it would be but just to make sure);
Unsubscribe from OnChange on the Stop() method;
You may also want to have a "lock" around changeListener.Start(_SQLStatement); and the Stop so that there are no racing conditions (I leave that one up to you to figure out if you need it or not);
Does this make sense ?
(Note: this is an over-simplified scenario to demonstrate my coding issue.)
I have the following class interface:
public class CustomerService
{
Task<IEnumerable<Customer>> FindCustomersInArea(String areaName);
Task<Customer> GetCustomerByName(String name);
:
}
This is the client-side of a RESTful API which loads a list of Customer objects from the server then exposes methods that allows client code to consume and work against that list.
Both of these methods work against the internal list of Customers retrieved from the server as follows:
private Task<IEnumerable<Customer>> LoadCustomersAsync()
{
var tcs = new TaskCompletionSource<IEnumerable<Customer>>();
try
{
// GetAsync returns Task<HttpResponseMessage>
Client.GetAsync(uri).ContinueWith(task =>
{
if (task.IsCanceled)
{
tcs.SetCanceled();
}
else if (task.IsFaulted)
{
tcs.SetException(task.Exception);
}
else
{
// Convert HttpResponseMessage to desired return type
var response = task.Result;
var list = response.Content.ReadAs<IEnumerable<Customer>>();
tcs.SetResult(list);
}
});
}
catch (Exception ex)
{
tcs.SetException(ex);
}
}
The Client class is a custom version of the HttpClient class from the WCF Web API (now ASP.NET Web API) because I am working in Silverlight and they don't have an SL version of their client assemblies.
After all that background, here's my problem:
All of the methods in the CustomerService class use the list returned by the asynchronous LoadCustomersAsync method; therefore, any calls to these methods should wait (asynchronously) until the LoadCustomers method has returned and the appopriate logic executed on the returned list.
I also only want one call made from the client (in LoadCustomers) at a time. So, I need all of the calls to the public methods to wait on the same internal task.
To review, here's what I need to figure out how to accomplish:
Any call to FindCustomersInArea and GetCustomerByName should return a Task that waits for the LoadCustomersAsync method to complete. If LoadCustomersAsync has already returned (and the cached list still valid), then the method may continue immediately.
After LoadCustomersAsync returns, each method has additional logic required to convert the list into the desired return value for the method.
There must only ever be one active call to LoadCustomersAsync (of the GetAsync method within).
If the cached list expires, then subsequent calls will trigger a reload (via LoadCustomersAsync).
Let me know if you need further clarification, but I'm hoping this is a common enough use case that someone can help me work out the logic to get the client working as desired.
Disclaimer: I'm going to assume you're using a singleton instance of your HttpClient subclass. If that's not the case we need only modify slightly what I'm about to tell you.
Yes, this is totally doable. The mechanism we're going to rely on for subsequent calls to LoadCustomersAsync is that if you attach a continuation to a Task, even if that Task completed eons ago, you're continuation will be signaled "immediately" with the task's final state.
Instead of creating/returning a new TaskCompletionSource<T> (TCS) every time from the LoadCustomerAsync method, you would instead have a field on the class that represents the TCS. This will allow your instance to remember the TCS that last represented the call that represented a cache-miss. This TCS's state will be signaled exactly the same as your existing code. You'll add the knowledge of whether or not the data has expired as another field which, combined with whether the TCS is currently null or not, will be the trigger for whether or not you actually go out and load the data again.
Ok, enough talk, it'll probably make a lot more sense if you see it.
The Code
public class CustomerService
{
// Your cache timeout (using 15mins as example, can load from config or wherever)
private static readonly TimeSpan CustomersCacheTimeout = new TimeSpan(0, 15, 0);
// A lock object used to provide thread safety
private object loadCustomersLock = new object();
private TaskCompletionSource<IEnumerable<Customer>> loadCustomersTaskCompletionSource;
private DateTime loadCustomersLastCacheTime = DateTime.MinValue;
private Task<IEnumerable<Customer>> LoadCustomersAsync()
{
lock(this.loadCustomersLock)
{
bool needToLoadCustomers = this.loadCustomersTaskCompletionSource == null
||
(this.loadCustomersTaskCompletionSource.Task.IsFaulted || this.loadCustomersTaskCompletionSource.Task.IsCanceled)
||
DateTime.Now - this.loadCustomersLastCacheTime.Value > CustomersService.CustomersCacheTimeout;
if(needToLoadCustomers)
{
this.loadCustomersTaskCompletionSource = new TaskCompletionSource<IEnumerable<Customer>>();
try
{
// GetAsync returns Task<HttpResponseMessage>
Client.GetAsync(uri).ContinueWith(antecedent =>
{
if(antecedent.IsCanceled)
{
this.loadCustomersTaskCompletionSource.SetCanceled();
}
else if(antecedent.IsFaulted)
{
this.loadCustomersTaskCompletionSource.SetException(antecedent.Exception);
}
else
{
// Convert HttpResponseMessage to desired return type
var response = antecedent.Result;
var list = response.Content.ReadAs<IEnumerable<Customer>>();
this.loadCustomersTaskCompletionSource.SetResult(list);
// Record the last cache time
this.loadCustomersLastCacheTime = DateTime.Now;
}
});
}
catch(Exception ex)
{
this.loadCustomersTaskCompletionSource.SetException(ex);
}
}
}
}
return this.loadCustomersTaskCompletionSource.Task;
}
Scenarios where the customers aren't loaded:
If it's the first call, the TCS will be null so the TCS will be created and customers fetched.
If the previous call faulted or was canceled, a new TCS will be created and the customers fetched.
If the cache timeout has expired, a new TCS will be created and the customers fetched.
Scenarios where the customers are loading/loaded:
If the customers are in the process of loading, the existing TCS's Task will be returned and any continuations added to the task using ContinueWith will be executed once the TCS has been signaled.
If the customers are already loaded, the existing TCS's Task will be returned and any continuations added to the task using ContinueWith will be executed as soon as the scheduler sees fit.
NOTE: I used a coarse grained locking approach here and you could theoretically improve performance with a reader/writer implementation, but it would probably be a micro-optimization in your case.
I think you should change the way you call Client.GetAsync(uri). Do it roughly like this:
Lazy<Task> getAsyncLazy = new Lazy<Task>(() => Client.GetAsync(uri));
And in your LoadCustomersAsync method you write:
getAsyncLazy.Value.ContinueWith(task => ...
This will ensure that GetAsync only gets called once and that everyone interested in its result will receive the same task.