public static Message DeserialiseMsg(RemoteMessageDTO msg, ProcessId actorId) { var sender = String.IsNullOrEmpty(msg.Sender) ? ProcessId.NoSender : new ProcessId(msg.Sender); var replyTo = String.IsNullOrEmpty(msg.ReplyTo) ? ProcessId.NoSender : new ProcessId(msg.ReplyTo); switch ((Message.TagSpec)msg.Tag) { case Message.TagSpec.UserReply: var content = DeserialiseMsgContent(msg); return new ActorResponse(content, content.GetType().AssemblyQualifiedName, actorId, sender, msg.RequestId, msg.Exception == "RESPERR"); case Message.TagSpec.UserAsk: return new ActorRequest(DeserialiseMsgContent(msg), actorId, replyTo, msg.RequestId); case Message.TagSpec.User: return new UserMessage(DeserialiseMsgContent(msg), sender, replyTo); case Message.TagSpec.UserTerminated: return (TerminatedMessage)DeserialiseMsgContent(msg); case Message.TagSpec.GetChildren: return UserControlMessage.GetChildren; case Message.TagSpec.StartupProcess: return SystemMessage.StartupProcess; case Message.TagSpec.ShutdownProcess: return (ShutdownProcessMessage)DeserialiseMsgContent(msg); case Message.TagSpec.Restart: return SystemMessage.Restart; case Message.TagSpec.Pause: return SystemMessage.Pause; case Message.TagSpec.Unpause: return SystemMessage.Unpause; case Message.TagSpec.DispatchWatch: return (SystemDispatchWatchMessage)DeserialiseMsgContent(msg); case Message.TagSpec.DispatchUnWatch: return (SystemDispatchUnWatchMessage)DeserialiseMsgContent(msg); case Message.TagSpec.Watch: return (SystemAddWatcherMessage)DeserialiseMsgContent(msg); case Message.TagSpec.UnWatch: return (SystemRemoveWatcherMessage)DeserialiseMsgContent(msg); } throw new Exception($"Unknown Message Tag: {msg.Tag}"); }
public static ProcessId Inbox(ProcessId hub, RelayMsg msg) { try { switch (msg.Tag) { case RelayMsg.MsgTag.Connected: SpawnConnection(msg, hub); break; case RelayMsg.MsgTag.Disconnected: kill(Self[msg.ConnectionId]); break; case RelayMsg.MsgTag.Inbound: case RelayMsg.MsgTag.Subscribe: case RelayMsg.MsgTag.Unsubscribe: fwd(SpawnConnection(msg, hub), msg); break; } } catch (Exception e) { tell(Errors, e); } return hub; }
public SystemChildFaultedMessage(ProcessId child, ProcessId sender, Exception exception, object message) { Child = child; Sender = sender; Exception = exception; Message = message; }
public ActorRequest(object message, ProcessId to, ProcessId replyTo, long requestId) { Message = message; To = to; ReplyTo = replyTo; RequestId = requestId; }
StrategyContext( StrategyState global, Exception exception, object message, ProcessId sender, ProcessId failedProcess, ProcessId parentProcess, IEnumerable<ProcessId> siblings, IEnumerable<ProcessId> affects, Time pause, Option<Directive> directive, Option<MessageDirective> messageDirective ) { bool isStop = directive == LanguageExt.Directive.Stop; Global = isStop ? StrategyState.Empty : global; Exception = exception; Message = message; Sender = sender; Self = failedProcess; ParentProcess = parentProcess; Siblings = siblings ?? Siblings; Affects = affects ?? Affects; Pause = isStop ? 0 * s : pause; Directive = directive; MessageDirective = messageDirective; }
/// <summary> /// Creates a new State computation that is primed with the data of a particular /// failure event. /// </summary> /// <param name="strategy">Strategy as a State computation</param> /// <param name="pid">Process ID that failed</param> /// <param name="sender">Process that sent the message that cause the failure</param> /// <param name="parent">Supervisor of the failed Process</param> /// <param name="siblings">The siblings of the failed Process</param> /// <param name="ex">Exception</param> /// <param name="msg">Message that caused the failure</param> /// <returns>State computation that can be invoked by passing it /// an object of StrategyState. This will result in a StateResult that contains /// the mutated StrategyState and a StrategyDecision. The StrategyDecision /// contains all the information needed to decide the fate of a Process (and /// related processes)</returns> public static State<StrategyState, StrategyDecision> Failure( this State<StrategyContext, Unit> strategy, ProcessId pid, ProcessId sender, ProcessId parent, IEnumerable<ProcessId> siblings, Exception ex, object msg ) { return stateInst => { var now = DateTime.UtcNow; var state = strategy(StrategyContext.Empty.With( Global: stateInst.With(Failures: stateInst.Failures + 1), FailedProcess: pid, ParentProcess: parent, Sender: sender, Siblings: siblings, Exception: ex, Message: msg)).State; var decision = new StrategyDecision( state.Directive.IfNone(Directive.Restart), state.MessageDirective.IfNone(MessageDirective.ForwardToDeadLetters), state.Affects, state.Pause ); return StateResult.Return(state.Global.With(LastFailure: now), decision); }; }
public ActorResponse(ProcessId replyTo, object message, ProcessId replyFrom, long requestId) { Message = message; ReplyTo = replyTo; ReplyFrom = replyFrom; RequestId = requestId; }
public AskActorReq(object msg, Subject<object> subject, ProcessId to, ProcessId replyTo) { Message = msg; Subject = subject; To = to; ReplyTo = replyTo; }
internal static RemoteMessageDTO Create(object message, ProcessId to, ProcessId sender, Message.Type type, Message.TagSpec tag, Option<SessionId> sessionId) => map(message as ActorRequest, req => req == null ? map(message as ActorResponse, res => res == null ? CreateMessage(message, to, sender, type, tag, sessionId) : CreateResponse(res, to, sender, sessionId)) : CreateRequest(req, to, sender, sessionId));
private DeadLetter(ProcessId sender, ProcessId recipient, Exception ex, string reason, object message) { Sender = sender; Recipient = recipient; Exception = Optional(ex); Reason = Optional(reason); Message = Optional(message); }
void SpawnResolver() { resolver = spawn<Map<ProcessId, BallState>, Tuple<ProcessId, BallState>>( "resolver", () => Map<ProcessId, BallState>(), Resolver.Inbox ); }
public ActorResponse(object message, string responseMessageType, ProcessId replyTo, ProcessId replyFrom, long requestId, bool isFaulted = false) { Message = message; ResponseMessageType = responseMessageType; ReplyTo = replyTo; ReplyFrom = replyFrom; RequestId = requestId; IsFaulted = isFaulted; }
/// <summary> /// Shutdown the process log /// </summary> public static Unit shutdown() { if (processId.IsValid) { kill(processId); processId = ProcessId.None; } return unit; }
public static ProcessId ConnectionInbox(ProcessId hub, RelayMsg rmsg) { switch (rmsg.Tag) { case RelayMsg.MsgTag.Inbound: var inmsg = rmsg as InboundRelayMsg; if (rmsg.IsAsk) { // Ask not supported tell(Errors, "'ask' not supported from JS to server."); } else { tell(rmsg.To, inmsg.Message, rmsg.Sender.IsValid ? Self.Append(rmsg.Sender) : ProcessId.NoSender); } break; case RelayMsg.MsgTag.Outbound: fwd(hub, rmsg); break; case RelayMsg.MsgTag.Subscribe: var pid = rmsg.To; var subscriber = rmsg.Sender; var connectionId = rmsg.ConnectionId; ActorContext.SelfProcess.Actor.AddSubscription( rmsg.To, ActorContext.Observe<object>(pid).Subscribe(x => tell(hub, new OutboundRelayMsg( connectionId, new RemoteMessageDTO { MessageId = Guid.NewGuid(), Content = JsonConvert.SerializeObject(x), Sender = pid.Path, To = subscriber.Path, ContentType = x.GetType().AssemblyQualifiedName, ReplyTo = pid.Path, Tag = (int)Message.TagSpec.User, Type = (int)Message.Type.User }, subscriber, pid, false), pid))); break; case RelayMsg.MsgTag.Unsubscribe: ActorContext.SelfProcess.Actor.RemoveSubscription(rmsg.To); break; } return hub; }
internal static ProcessId WatchWorkers(ProcessId router, IEnumerable<ProcessId> workers, RouterOption option) { if ((option & RouterOption.RemoveLocalWorkerWhenTerminated) == RouterOption.RemoveLocalWorkerWhenTerminated) { workers.Where(w => ActorContext.IsLocal(w)).Iter(w => watch(router, w)); } if ((option & RouterOption.RemoveRemoteWorkerWhenTerminated) == RouterOption.RemoveRemoteWorkerWhenTerminated) { workers.Where(w => !ActorContext.IsLocal(w)).Iter(w => watch(router, w)); } return router; }
public static Unit RemoveFromStore(ProcessId id) { lock (storeLock) { var path = id.Value; if (Store.ContainsKey(path)) { Store = Store.Remove(path); } } return unit; }
public static Unit AddToStore(ProcessId id, IProcess process) { lock (storeLock) { var path = id.Value; if (Store.ContainsKey(path)) { Store[path].Dispose(); Store = Store.Remove(path); } Store = Store.Add(path, process); } return unit; }
internal static RemoteMessageDTO CreateRequest(ActorRequest req, ProcessId to, ProcessId sender) => new RemoteMessageDTO() { Type = (int)Message.Type.User, Tag = (int)Message.TagSpec.UserAsk, Child = null, Exception = null, To = to.Path, RequestId = req.RequestId, MessageId = Guid.NewGuid(), Sender = sender.ToString(), ReplyTo = req.ReplyTo.ToString(), ContentType = req.Message.GetType().AssemblyQualifiedName, Content = JsonConvert.SerializeObject(req.Message, ActorConfig.Default.JsonSerializerSettings) };
public ActorRequestContext( ActorItem self, ProcessId sender, ActorItem parent, object currentMsg, ActorRequest currentRequest, ProcessFlags processFlags ) { Self = self; Sender = sender; Parent = parent; CurrentMsg = currentMsg; CurrentRequest = currentRequest; ProcessFlags = processFlags; }
/// <summary> /// Start up the process log /// </summary> /// <param name="processNameOverride">Override the default process name</param> /// <param name="logViewMax">Size of the log 'window'</param> public static Unit startup(Option<ProcessName> processNameOverride, int logViewMax = 200) { if (processId.IsValid) return unit; lock (sync) { if (processId.IsValid) return unit; processName = processNameOverride.IfNone("process-log"); processId = spawn<State, ProcessLogItem>(processName, () => setup(logViewMax), inbox); deadLetterSub = subscribe<DeadLetter>(DeadLetters, msg => tellWarning(msg.ToString())); errorSub = subscribe<Exception>(Errors, e => tellError(e)); } return unit; }
internal static RemoteMessageDTO CreateMessage(object message, ProcessId to, ProcessId sender, Message.Type type, Message.TagSpec tag) => new RemoteMessageDTO { Type = (int)type, Tag = (int)tag, To = to.Path, RequestId = -1, MessageId = Guid.NewGuid(), Sender = sender.ToString(), ReplyTo = sender.ToString(), ContentType = message == null ? null : message.GetType().AssemblyQualifiedName, Content = message == null ? null : JsonConvert.SerializeObject(message, ActorSystemConfig.Default.JsonSerializerSettings) };
static BallState Detect(ProcessId pidA, BallState stateA, ProcessId pidB, BallState stateB, BallState lastStateB) { if (pidA == pidB) return stateB; var a = new Point2(stateA.X, stateA.Y); var b = new Point2(stateB.X, stateB.Y); var avel = new Vector2(stateA.VelX.MetresPerSecond, stateA.VelY.MetresPerSecond); var bvel = new Vector2(stateB.VelX.MetresPerSecond, stateB.VelY.MetresPerSecond); Vector2 collision = a - b; double distance = collision.Length; if (distance == 0.0) { // hack to avoid div by zero collision = new Vector2(1.0, 0.0); distance = 1.0; } if (distance > stateA.BallSize && distance > stateB.BallSize) { return stateB; } // Get the components of the velocity vectors which are parallel to the collision. // The perpendicular component remains the same for both fish collision = collision / distance; double aci = Vector2.Dot(avel, collision); double bci = Vector2.Dot(bvel, collision); // Solve for the new velocities using the 1-dimensional elastic collision equations. // Turns out it's really simple when the masses are the same. double acf = bci; double bcf = aci; // Replace the collision velocity components with the new ones var navel = avel + collision * (acf - aci); var nbvel = bvel + collision * (bcf - bci); SetPosAndVelMsg.Tell(pidA, a.X, a.Y, navel.DX*m/s, navel.DY*m/s); SetPosAndVelMsg.Tell(pidB, lastStateB.X, lastStateB.Y, nbvel.DX*m/s, nbvel.DY*m/s); return lastStateB; }
public static Message DeserialiseMsg(RemoteMessageDTO msg, ProcessId actorId) { var sender = String.IsNullOrEmpty(msg.Sender) ? ProcessId.NoSender : new ProcessId(msg.Sender); var replyTo = String.IsNullOrEmpty(msg.ReplyTo) ? ProcessId.NoSender : new ProcessId(msg.ReplyTo); switch ((Message.TagSpec)msg.Tag) { case Message.TagSpec.UserReply: var content = DeserialiseMsgContent(msg); return new ActorResponse(content, content.GetType().AssemblyQualifiedName, actorId, sender, msg.RequestId, msg.Exception == "RESPERR"); case Message.TagSpec.UserAsk: return new ActorRequest(DeserialiseMsgContent(msg), actorId, replyTo, msg.RequestId); case Message.TagSpec.User: return new UserMessage(DeserialiseMsgContent(msg), sender, replyTo); case Message.TagSpec.GetChildren: return UserControlMessage.GetChildren; case Message.TagSpec.ShutdownProcess: return SystemMessage.ShutdownProcess; case Message.TagSpec.Restart: return SystemMessage.Restart; } throw new Exception("Unknown Message Type: " + msg.Type); }
public ActorRequestContext( ActorSystem system, ActorItem self, ProcessId sender, ActorItem parent, object currentMsg, ActorRequest currentRequest, ProcessFlags processFlags, ProcessOpTransaction ops, SessionVector session ) { Self = self; Sender = sender; Parent = parent; CurrentMsg = currentMsg; CurrentRequest = currentRequest; ProcessFlags = processFlags; Ops = ops; System = system; Session = session; }
public StrategyContext With( StrategyState Global = null, Exception Exception = null, object Message = null, ProcessId? Sender = null, ProcessId? FailedProcess = null, ProcessId? ParentProcess = null, IEnumerable<ProcessId> Siblings = null, IEnumerable<ProcessId> Affects = null, Time? Pause = null, Option<Directive> Directive = default(Option<Directive>), Option<MessageDirective> MessageDirective = default(Option<MessageDirective>) ) => new StrategyContext( Global ?? this.Global, Exception ?? this.Exception, Message ?? this.Message, Sender ?? this.Sender, FailedProcess ?? this.Self, ParentProcess ?? this.ParentProcess, Siblings ?? this.Siblings, Affects ?? this.Affects, Pause ?? this.Pause, // The following may look like bugs, but it's intentional that when // a Directive or MessageDirective is set, they stay set to their first // concrete value. That means the State strategy expression that runs // has the equivalent of an 'early out'. The whole expression is still // processed, but you can't override the earlier value. this.Directive.IsSome ? this.Directive : Directive, this.MessageDirective.IsSome ? this.MessageDirective : MessageDirective );
public Unit DispatchUnWatch(ProcessId watching) => TellSystem(SystemMessage.DispatchUnWatch(watching), watching);
/// <summary> /// Create a new dead letter /// </summary> public static DeadLetter create(ProcessId sender, ProcessId recipient, object message) => new DeadLetter(sender, recipient, null, null, message);
public TerminatedMessage(ProcessId id) { Id = id; }
public Unit Tell(object message, ProcessId sender, Message.TagSpec tag) => Tell(message, sender, "user", Message.Type.User, tag);
/// <summary> /// Subscribe to the process publish stream. When a process calls 'publish' it emits /// messages that can be consumed using this method. /// </summary> /// <remarks> /// The process can publish any number of types, any published messages not of type T will be ignored. /// /// Because this call is asychronous it could allow access to the message loop, therefore /// you can't call it from within a process message loop. /// </remarks> /// <returns>IDisposable, call IDispose to end the subscription</returns> public static IDisposable subscribe <T>(ProcessId pid, Action <T> onNext, Action onComplete) => InMessageLoop ? raiseDontUseInMessageLoopException <IDisposable>(nameof(subscribe)) : observe <T>(pid).Subscribe(onNext, ex => { }, onComplete);
public Unit Tell(object message, ProcessId sender, string inbox, Message.Type type, Message.TagSpec tag) => ProcessOp.IO(() => TellNoIO(message, sender, inbox, type, tag));
/// <summary> /// Subscribe to the process publish stream. When a process calls 'publish' it emits /// messages that can be consumed using this method. /// </summary> /// <remarks> /// The process can publish any number of types, any published messages not of type T will be ignored. /// /// Because this call is asychronous it could allow access to the message loop, therefore /// you can't call it from within a process message loop. /// </remarks> /// <returns>IDisposable, call IDispose to end the subscription</returns> public static IDisposable subscribe <T>(ProcessId pid, IObserver <T> observer) => InMessageLoop ? raiseDontUseInMessageLoopException <IDisposable>(nameof(subscribe)) : observe <T>(pid).Subscribe(observer);
/// <summary> /// Subscribes our inbox to another process publish stream. When it calls 'publish' it will /// arrive in our inbox. /// </summary> /// <param name="pid">Process to subscribe to</param> /// <remarks> /// The process can publish any number of types, any published messages not of type T will be ignored. /// /// This should be used from within a process' message loop only /// </remarks> /// <returns>IDisposable, call IDispose to end the subscription</returns> public static Unit subscribe(ProcessId pid) => InMessageLoop ? ActorContext.SelfProcess.Actor.AddSubscription(pid, ActorContext.Observe <object>(pid).Subscribe(x => tell(Self, x, pid))) : raiseUseInMsgLoopOnlyException <Unit>(nameof(subscribe));
public Unit UnWatch(ProcessId pid) => TellSystem(SystemMessage.UnWatch(pid), pid);
/// <summary> /// Spawn an update process that sends a time message to the ball /// process (via state subscription) every 60th of a second /// </summary> void SpawnUpdate() { update = spawn<DateTime, DateTime>("update", () => DateTime.Now, (state,time) => { tellSelf(DateTime.Now, 1 * second / 60); return time; }); // Go! tell(update, DateTime.Now); }
/// <summary> /// Create a new dead letter /// </summary> public static DeadLetter create(ProcessId sender, ProcessId recipient, Exception ex, string reason, object message) => new DeadLetter(sender, recipient, ex, reason, message);
public UserMessage(object message, ProcessId sender, ProcessId replyTo) { Content = message; Sender = sender; ReplyTo = replyTo; }
internal static RemoteMessageDTO CreateResponse(ActorResponse res, ProcessId to, ProcessId sender, Option <SessionId> sessionId) => new RemoteMessageDTO { Type = (int)Message.Type.User, Tag = (int)Message.TagSpec.UserReply, Child = null, Exception = res.IsFaulted ? "RESPERR" : null, To = to.ToString(), RequestId = res.RequestId, MessageId = Guid.NewGuid(), Sender = res.ReplyFrom.ToString(), ReplyTo = res.ReplyTo.ToString(), ContentType = res.Message.GetType().AssemblyQualifiedName, Content = JsonConvert.SerializeObject(res.Message, ActorSystemConfig.Default.JsonSerializerSettings), SessionId = sessionId.Map(s => s.Value).IfNoneUnsafe(() => null) };
/// <summary> /// Ctor /// </summary> public ProcessInboxFullException(ProcessId pid, int maximumSize, string type) : base("Process (" + pid + ") " + type + " inbox is full (Maximum items: " + maximumSize + ")") { }
/// <summary> /// Ask a process for a reply /// </summary> /// <param name="pid">Process to ask</param> /// <param name="message">Message to send</param> /// <returns>The response to the request</returns> public static T ask <T>(ProcessId pid, object message) => ActorContext.Ask <T>(pid, message);
public static string ClusterMetaDataKey(ProcessId pid) => ClusterKey(pid) + "-metadata";
/// <summary> /// Unsubscribe from a process's publications /// </summary> /// <param name="pid">Process to unsub from</param> public static Unit unsubscribe(ProcessId pid) => InMessageLoop ? ActorContext.SelfProcess.Actor.RemoveSubscription(pid) : raiseUseInMsgLoopOnlyException <Unit>(nameof(unsubscribe));
public static string ClusterSystemInboxNotifyKey(ProcessId pid) => ClusterInboxNotifyKey(pid, "system");
/// <summary> /// Get an IObservable for a process's state stream. When a process state updates at the end of its /// message loop it announces it on the stream returned from this method. You should use this for /// notification only. Never modify the state object belonging to a process. Best practice is to make /// the state type immutable. /// </summary> /// <remarks> /// The process can publish any number of types, any published messages not of type T will be ignored. /// /// Because this call is asychronous it could allow access to the message loop, therefore /// you can't call it from within a process message loop. /// </remarks> /// <returns>IObservable T</returns> public static IObservable <T> observeState <T>(ProcessId pid) => InMessageLoop ? raiseDontUseInMessageLoopException <IObservable <T> >(nameof(observeState)) : ActorContext.ObserveState <T>(pid);
public static string ClusterUserInboxNotifyKey(ProcessId pid) => ClusterInboxNotifyKey(pid, "user");
/// <summary> /// Subscribe to the process publish stream. When a process calls 'publish' it emits /// messages that can be consumed using this method. /// </summary> /// <remarks> /// The process can publish any number of types, any published messages not of type T will be ignored. /// /// Because this call is asychronous it could allow access to the message loop, therefore /// you can't call it from within a process message loop. /// </remarks> public static IDisposable subscribe <T>(ProcessId pid, Action <T> onNext, Action <Exception> onError) => InMessageLoop ? raiseDontUseInMessageLoopException <IDisposable>(nameof(subscribe)) : observe <T>(pid).Subscribe(onNext, onError, () => { });
public static string ClusterInboxNotifyKey(ProcessId pid, string type) => ClusterInboxKey(pid, type) + "-notify";
public Unit TellSystem(SystemMessage message, ProcessId sender) => ProcessOp.IO(() => { var dto = RemoteMessageDTO.Create(message, ProcessId, sender, Message.Type.System, message.Tag, SessionId); var clientsReached = Cluster.PublishToChannel(ActorInboxCommon.ClusterSystemInboxNotifyKey(ProcessId), dto); });
public static string ClusterInboxKey(ProcessId pid, string type) => ClusterKey(pid) + "-" + type + "-inbox";
public Unit TellUserControl(UserControlMessage message, ProcessId sender) => Tell(message, sender, "user", Message.Type.UserControl, message.Tag);
public static string ClusterSettingsKey(ProcessId pid) => ClusterKey(pid) + "@settings";
public ActorDispatchRemote(ProcessId pid, ICluster cluster, Option <SessionId> sessionId) { ProcessId = pid; Cluster = cluster; SessionId = sessionId; }
public static string ClusterKey(ProcessId pid) => pid.Path;
public static Option <Tuple <RemoteMessageDTO, Message> > GetNextMessage(ICluster cluster, ProcessId self, string key) { if (cluster == null) { return(None); } Message msg = null; RemoteMessageDTO dto = null; dto = null; do { dto = cluster.Peek <RemoteMessageDTO>(key); if (dto == null) { // Queue is empty return(None); } if (dto.Tag == 0 && dto.Type == 0) { // Message is bad cluster.Dequeue <RemoteMessageDTO>(key); tell(ActorContext.System(self).DeadLetters, DeadLetter.create(dto.Sender, self, null, "Failed to deserialise message: ", dto)); if (cluster.QueueLength(key) == 0) { return(None); } } }while (dto == null || dto.Tag == 0 || dto.Type == 0); try { msg = MessageSerialiser.DeserialiseMsg(dto, self); } catch (Exception e) { // Message can't be deserialised cluster.Dequeue <RemoteMessageDTO>(key); tell(ActorContext.System(self).DeadLetters, DeadLetter.create(dto.Sender, self, e, "Failed to deserialise message: ", msg)); return(None); } return(Some(Tuple(dto, msg))); }
private static string ProcessFmt(ProcessId pid) => pid.IsValid ? pid.ToString() : "no-sender";
public static Option <UserControlMessage> PreProcessMessage <T>(ProcessId sender, ProcessId self, object message) { if (message == null) { var emsg = $"Message is null for tell (expected {typeof(T)})"; tell(ActorContext.System(self).DeadLetters, DeadLetter.create(sender, self, emsg, message)); return(None); } if (message is ActorRequest) { var req = (ActorRequest)message; if (!(req.Message is T) && !(req.Message is Message)) { var emsg = $"Invalid message type for ask (expected {typeof(T)})"; tell(ActorContext.System(self).DeadLetters, DeadLetter.create(sender, self, emsg, message)); ActorContext.System(self).Tell( sender, new ActorResponse(new Exception($"Invalid message type for ask (expected {typeof(T)})"), typeof(Exception).AssemblyQualifiedName, sender, self, req.RequestId, true ), self ); return(None); } return(Optional((UserControlMessage)message)); } return(new UserMessage(message, sender, sender)); }
public static string ClusterStatePubSubKey(ProcessId pid) => ClusterKey(pid) + "-state-pubsub";