Replies: 5 comments
-
@wanton7 If I understand correctly, you want to write non-actor code for getting data from an actor. There are a few ways to do this. A sample that follows the Synchronous Execution document is below. using System;
using Microsoft.Coyote;
using Microsoft.Coyote.Actors;
using Microsoft.Coyote.Specifications;
using Microsoft.Coyote.SystematicTesting;
using System.Threading.Tasks;
namespace CoyoteTest
{
public class RequestEvent : Event
{
public string Request;
public ActorId Sender;
public RequestEvent(ActorId sender, string request)
{
this.Sender = sender;
this.Request = request;
}
}
public class ResponseEvent : Event
{
public string Response;
public ResponseEvent(string response)
{
this.Response = response;
}
}
[OnEventDoAction(typeof(RequestEvent), nameof(RequestHandler))]
public class RequestResponseActor : Actor
{
private void RequestHandler(Event request)
{
if (request is RequestEvent req)
{
var response = ProcessRequest(req.Request);
this.SendEvent(req.Sender, new ResponseEvent(response));
}
}
protected virtual string ProcessRequest(string request)
{
throw new NotImplementedException();
}
}
public class SimpleAckingActor : RequestResponseActor
{
protected override string ProcessRequest(string request)
{
return "Ack: " + request;
}
}
static class Helper
{
private class FetchResponseEvent : Event
{
public ActorId TargetActorId;
public string Request;
public ResponseEvent Response;
}
private class GetResponseActor : Actor
{
protected override async Task OnInitializeAsync(Event initialEvent)
{
if (initialEvent is FetchResponseEvent ev)
{
this.SendEvent(ev.TargetActorId, new RequestEvent(this.Id, ev.Request));
var resp = await this.ReceiveEventAsync(typeof(ResponseEvent)) as ResponseEvent;
ev.Response = resp;
this.RaiseHaltEvent();
}
}
}
public static async Task<ResponseEvent> GetResponse(ActorId targetActor, string request)
{
var fetchResponseEvent = new FetchResponseEvent() { TargetActorId = targetActor, Request = request, Response = null };
await targetActor.Runtime.CreateActorAndExecuteAsync(
typeof(GetResponseActor),
initialEvent: fetchResponseEvent
);
return fetchResponseEvent.Response;
}
}
public class Program
{
public static void Main(string[] args)
{
IActorRuntime runtime = RuntimeFactory.Create();
Execute(runtime).Wait();
Console.ReadLine();
}
[Test]
public static async Task Execute(IActorRuntime runtime)
{
ActorId id = runtime.CreateActor(typeof(SimpleAckingActor));
var response = await Helper.GetResponse(id, "hello");
Console.WriteLine("Received response: " + response.Response);
}
}
} The static method Another way is to create a |
Beta Was this translation helpful? Give feedback.
-
Here's the code for the other way using using System;
using Microsoft.Coyote;
using Microsoft.Coyote.Actors;
using Microsoft.Coyote.Specifications;
using Microsoft.Coyote.SystematicTesting;
using Microsoft.Coyote.Tasks;
namespace CoyoteTest
{
public class RequestEvent<TRequest, TResult> : Event
{
public TRequest Request;
public Microsoft.Coyote.Tasks.TaskCompletionSource<TResult> Completed = TaskCompletionSource.Create<TResult>();
public RequestEvent(TRequest request)
{
this.Request = request;
}
}
public class RequestResponseActor<TRequest, TResult> : Actor
{
private RequestEvent<TRequest, TResult> Request;
protected override System.Threading.Tasks.Task OnInitializeAsync(Event initialEvent)
{
if (initialEvent is RequestEvent<TRequest, TResult> req)
{
this.Request = req;
ProcessRequest(req.Request);
}
return base.OnInitializeAsync(initialEvent);
}
protected virtual void ProcessRequest(TRequest request)
{
throw new NotImplementedException();
}
protected void FinishRequest(TResult response)
{
this.Request.Completed.SetResult(response);
}
}
[OnEventDoAction(typeof(PingEvent), nameof(HandlePing))]
public class PingPongServer : Actor
{
public class PingEvent : Event
{
public string Message;
public ActorId Caller;
}
public class PongEvent : Event
{
public string Message;
}
private void HandlePing(Event e)
{
if (e is PingEvent p)
{
this.SendEvent(p.Caller, new PongEvent() { Message = "Received: " + p.Message });
}
}
}
[OnEventDoAction(typeof(PingPongServer.PongEvent), nameof(HandlePong))]
public class ExampleHttpServer : RequestResponseActor<string, string>
{
protected override void ProcessRequest(string request)
{
var id = this.CreateActor(typeof(PingPongServer));
this.SendEvent(id, new PingPongServer.PingEvent() { Caller = this.Id, Message = request });
}
public void HandlePong(Event e)
{
string msg = ((PingPongServer.PongEvent)e).Message;
this.FinishRequest(msg);
}
}
public class Program
{
public static void Main(string[] args)
{
IActorRuntime runtime = RuntimeFactory.Create();
Execute(runtime);
Console.ReadLine();
}
[Test]
public static async Microsoft.Coyote.Tasks.Task Execute(IActorRuntime runtime)
{
MyController controller = new MyController(runtime);
await controller.DoSomething("Hello world!");
}
}
class MyController
{
IActorRuntime runtime;
public MyController(IActorRuntime runtime)
{
this.runtime = runtime;
}
[HttpGet]
public async Microsoft.Coyote.Tasks.Task<ActionResult> DoSomething(string name)
{
var request = new RequestEvent<string, string>(name);
ActorId id = runtime.CreateActor(typeof(ExampleHttpServer), request);
var response = await request.Completed.Task;
Console.WriteLine("Received response: " + response);
return View(response);
}
ActionResult View(string s)
{
return new ActionResult();
}
}
} |
Beta Was this translation helpful? Give feedback.
-
@akashlal and @lovettchris thanks that is what I needed know. Btw if there is exception and runtime crashes is ok not to restart process and instead just to create new actor runtime and replace old one in production? What happens to |
Beta Was this translation helpful? Give feedback.
-
Right an unhandled exception will stop the ActorRuntime and the OnFailure event will be raised on the IActorRuntime. After that you might need to create a new ActorRuntime for future requests if you want to be really sure the ActorRuntime is not in a bad state, but simple tests seem to indicate the ActorRuntime continues to function just fine after unhandled exceptions, I believe the Actors that threw those exceptions are automatically halted. You could use OnFailure to cancel the TaskCompletionSource. You could also put a timeout on the Wait matching your Http request timeout. |
Beta Was this translation helpful? Give feedback.
-
If have event go through multiple actors I think it quite hard to know that everything is still ok in actor system when unhandled exception happens. From what I've read with proper Coyote testing you can get most bugs our and I consider unhandled exceptions as bugs. In rare case unhandled exception would happen I would like have restart code in place just to be sure. I think I got all my answers related to this, thanks for you both. |
Beta Was this translation helpful? Give feedback.
-
I like to integrate Coyote with ASP.NET Core REST API we have. I was reading
Synchronous execution of actors
in docs and is it so that I have to create an actor that is created just for calling into actor system?If I would use
CreateActorAndExecuteAsync
and send message in itsOnEntry
method should callHalt()
at end of that method if I'm gonna use that actor for sending a single message from REST API?It would be create I there was example/sample how to integrate something similar to ASP.NET Core API to Coyote actor system. Just having having example how to send single message into actor system and receive value synchronously that could be used without blocking from ASP.NET Core controller would be enough.
Beta Was this translation helpful? Give feedback.
All reactions