/// <summary> /// Cancel an already scheduled message /// </summary> public static Unit cancelScheduled(ProcessId pid, string key) { var inboxKey = ActorInboxCommon.ClusterScheduleKey(pid); LocalScheduler.RemoveExistingScheduledMessage(pid, key); return(tell(pid.Take(1).Child("system").Child("scheduler"), Scheduler.Msg.RemoveFromSchedule(inboxKey, key))); }
static State Check(State state, ICluster cluster) { var now = DateTime.UtcNow.Ticks; foreach (var map in state.Scheduled) { foreach (var outerKeyValue in map) { foreach (var kv in outerKeyValue.Value) { if (kv.Value.Due < now) { var inboxKey = ActorInboxCommon.ClusterInboxKey(kv.Value.To, "user"); var inboxNotifyKey = ActorInboxCommon.ClusterInboxNotifyKey(kv.Value.To, "user"); if (cluster.Enqueue(inboxKey, kv.Value) > 0) { cluster.DeleteHashField(outerKeyValue.Key, kv.Key); state = state.Delete(outerKeyValue.Key, kv.Key); cluster.PublishToChannel(inboxNotifyKey, kv.Value.MessageId); } } } } } return(state); }
public Unit Tell(object message, ProcessId sender, Option <SessionId> sessionId) { if (message == null) { throw new ArgumentNullException(nameof(message)); } if (userInbox != null) { ActorInboxCommon.PreProcessMessage <T>(sender, actor.Id, message, sessionId) .IfSome(msg => { if (IsPaused) { new ActorDispatchRemote(ActorContext.System(actor.Id).Ping, actor.Id, cluster, ActorContext.SessionId, false).Tell(message, Schedule.Immediate, sender, Message.TagSpec.User); } else { try { userInbox?.Post(msg); } catch (QueueFullException) { throw new ProcessInboxFullException(actor.Id, MailboxSize, "user"); } } }); } return(unit); }
public Unit Ask(object message, ProcessId sender, Option <SessionId> sessionId) { if (userInbox != null) { ActorInboxCommon.PreProcessMessage <T>(sender, actor.Id, message, sessionId) .IfSome(msg => { if (IsPaused) { new ActorDispatchRemote(ActorContext.System(actor.Id).Ping, actor.Id, cluster, ActorContext.SessionId, false).Ask(message, sender); } else { try { userInbox?.Post(msg); } catch (QueueFullException) { throw new ProcessInboxFullException(actor.Id, MailboxSize, "user"); } } }); } return(unit); }
public void CheckRemoteInbox(string key, ICluster cluster, ProcessId self, PausableBlockingQueue <SystemMessage> sysInbox, PausableBlockingQueue <UserControlMessage> userInbox, bool pausable) { try { int count = cluster.QueueLength(key); while (count > 0 && (!pausable || !IsPaused)) { Option <Tuple <RemoteMessageDTO, Message> > pair; lock (sync) { pair = ActorInboxCommon.GetNextMessage(cluster, self, key); pair.IfSome(x => cluster.Dequeue <RemoteMessageDTO>(key)); } pair.IfSome(x => iter(x, (dto, msg) => { switch (msg.MessageType) { case Message.Type.System: sysInbox.Post((SystemMessage)msg); break; case Message.Type.User: userInbox.Post((UserControlMessage)msg); break; case Message.Type.UserControl: userInbox.Post((UserControlMessage)msg); break; } })); count--; } } catch (Exception e) { logSysErr($"CheckRemoteInbox failed for {self}", e); } }
public Unit Startup(IActor process, ActorItem parent, Option <ICluster> cluster, int maxMailboxSize) { if (cluster.IsNone) { throw new Exception("Remote inboxes not supported when there's no cluster"); } this.actor = (Actor <S, T>)process; this.cluster = cluster.IfNoneUnsafe(() => null); this.maxMailboxSize = maxMailboxSize == -1 ? ActorContext.System(actor.Id).Settings.GetProcessMailboxSize(actor.Id) : maxMailboxSize; this.parent = parent; userNotify = new PausableBlockingQueue <string>(this.maxMailboxSize); var obj = new ThreadObj { Actor = actor, Inbox = this, Parent = parent }; userNotify.ReceiveAsync(obj, (state, msg) => { CheckRemoteInbox(ActorInboxCommon.ClusterUserInboxKey(state.Actor.Id), true); return(InboxDirective.Default); }); SubscribeToSysInboxChannel(); SubscribeToUserInboxChannel(); this.cluster.SetValue(ActorInboxCommon.ClusterMetaDataKey(actor.Id), new ProcessMetaData( new[] { typeof(T).AssemblyQualifiedName }, typeof(S).AssemblyQualifiedName, typeof(S).GetTypeInfo().ImplementedInterfaces.Map(x => x.AssemblyQualifiedName).ToArray() )); return(unit); }
public Unit Startup(IActor process, ActorItem parent, Option <ICluster> cluster, int maxMailboxSize) { if (cluster.IsNone) { throw new Exception("Remote inboxes not supported when there's no cluster"); } this.actor = (Actor <S, T>)process; this.parent = parent; this.cluster = cluster.IfNoneUnsafe(() => null); this.maxMailboxSize = maxMailboxSize == -1 ? ActorContext.System(actor.Id).Settings.GetProcessMailboxSize(actor.Id) : maxMailboxSize; userInbox = new PausableBlockingQueue <UserControlMessage>(this.maxMailboxSize); sysInbox = new PausableBlockingQueue <SystemMessage>(this.maxMailboxSize); var obj = new ThreadObj { Actor = actor, Inbox = this, Parent = parent }; userInbox.ReceiveAsync(obj, (state, msg) => ActorInboxCommon.UserMessageInbox(state.Actor, state.Inbox, msg, state.Parent)); sysInbox.ReceiveAsync(obj, (state, msg) => ActorInboxCommon.SystemMessageInbox(state.Actor, state.Inbox, msg, state.Parent)); SubscribeToSysInboxChannel(); SubscribeToUserInboxChannel(); return(unit); }
void SubscribeToSysInboxChannel() { // System inbox is just listening to the notifications, that means that system // messages don't persist. cluster.UnsubscribeChannel(ActorInboxCommon.ClusterSystemInboxNotifyKey(actor.Id)); cluster.SubscribeToChannel <RemoteMessageDTO>(ActorInboxCommon.ClusterSystemInboxNotifyKey(actor.Id)).Subscribe(SysInbox); }
Unit DoScheduleNoIO(object message, ProcessId sender, Schedule schedule, Message.Type type, Message.TagSpec tag) { var inboxKey = ActorInboxCommon.ClusterScheduleKey(ProcessId); var id = schedule.Key ?? Guid.NewGuid().ToString(); var current = Cluster.GetHashField <RemoteMessageDTO>(inboxKey, id); message = current.Match( Some: last => { var a = MessageSerialiser.DeserialiseMsg(last, ProcessId) as UserMessage; var m = a == null ? message : schedule.Fold(a.Content, message); return(m); }, None: () => schedule.Fold(schedule.Zero, message)); ValidateMessageType(message, sender); var dto = RemoteMessageDTO.Create(message, ProcessId, sender, type, tag, SessionId, schedule.Due.Ticks); tell(ProcessId.Take(1).Child("system").Child("scheduler"), Scheduler.Msg.AddToSchedule(inboxKey, id, dto)); //Cluster.HashFieldAddOrUpdate(inboxKey, id, dto); return(unit); }
public Unit Startup(IActor process, ActorItem parent, Option <ICluster> cluster, int maxMailboxSize) { if (Active) { Shutdown(); } this.cluster = cluster; this.parent = parent; this.actor = (Actor <S, T>)process; this.maxMailboxSize = maxMailboxSize == -1 ? ActorContext.System(actor.Id).Settings.GetProcessMailboxSize(actor.Id) : maxMailboxSize; userInbox = new PausableBlockingQueue <UserControlMessage>(this.maxMailboxSize); sysInbox = new PausableBlockingQueue <SystemMessage>(this.maxMailboxSize); var obj = new ThreadObj { Actor = actor, Inbox = this, Parent = parent }; userInbox.ReceiveAsync(obj, (state, msg) => process.CancellationTokenSource.IsCancellationRequested ? InboxDirective.Shutdown : ActorInboxCommon.UserMessageInbox(state.Actor, state.Inbox, msg, state.Parent)); sysInbox.ReceiveAsync(obj, (state, msg) => ActorInboxCommon.SystemMessageInbox(state.Actor, state.Inbox, msg, state.Parent)); return(unit); }
/// <summary> /// Re-schedule an already scheduled message /// </summary> public static Unit reschedule(ProcessId pid, string key, DateTime when) { var inboxKey = ActorInboxCommon.ClusterScheduleKey(pid); LocalScheduler.Reschedule(pid, key, when); return(tell(pid.Take(1).Child("system").Child("scheduler"), Scheduler.Msg.Reschedule(inboxKey, key, when))); }
void SubscribeToUserInboxChannel() { cluster.UnsubscribeChannel(ActorInboxCommon.ClusterUserInboxNotifyKey(actor.Id)); cluster.SubscribeToChannel <string>(ActorInboxCommon.ClusterUserInboxNotifyKey(actor.Id)).Subscribe(msg => userNotify.Post(msg)); // We want the check done asyncronously, in case the setup function creates child processes that // won't exist if we invoke directly. cluster.PublishToChannel(ActorInboxCommon.ClusterUserInboxNotifyKey(actor.Id), Guid.NewGuid().ToString()); }
public void Dispose() { userInbox?.Cancel(); sysInbox?.Cancel(); cluster?.UnsubscribeChannel(ActorInboxCommon.ClusterUserInboxNotifyKey(actor.Id)); cluster?.UnsubscribeChannel(ActorInboxCommon.ClusterSystemInboxNotifyKey(actor.Id)); userInbox = null; sysInbox = null; cluster = null; }
void SubscribeToScheduleInboxChannel() { cluster.UnsubscribeChannel(ActorInboxCommon.ClusterScheduleNotifyKey(actor.Id)); cluster.SubscribeToChannel <string>(ActorInboxCommon.ClusterScheduleNotifyKey(actor.Id)).Subscribe(msg => scheduledItems++); // We want the check done asyncronously, in case the setup function creates child processes that // won't exist if we invoke directly. // TODO: Consider the implications of race condition here --- will probably need some large 'clear out' process that does a query // on the cluster. Or maybe this internal counter isn't the best approach. scheduledItems = cluster.GetHashFields(ActorInboxCommon.ClusterScheduleKey(actor.Id)).Count; }
public Unit TellSystem(SystemMessage message, ProcessId sender) => transactionalIO ? ignore(Cluster.PublishToChannel( ActorInboxCommon.ClusterSystemInboxNotifyKey(ProcessId), RemoteMessageDTO.Create(message, ProcessId, sender, Message.Type.System, message.Tag, SessionId, 0))) : ProcessOp.IO(() => { var clientsReached = Cluster.PublishToChannel( ActorInboxCommon.ClusterSystemInboxNotifyKey(ProcessId), RemoteMessageDTO.Create(message, ProcessId, sender, Message.Type.System, message.Tag, SessionId, 0)); return(unit); });
Unit TellNoIO(object message, ProcessId sender, string inbox, Message.Type type, Message.TagSpec tag) { ValidateMessageType(message, sender); var dto = RemoteMessageDTO.Create(message, ProcessId, sender, type, tag, SessionId, 0); var inboxKey = ActorInboxCommon.ClusterInboxKey(ProcessId, inbox); var inboxNotifyKey = ActorInboxCommon.ClusterInboxNotifyKey(ProcessId, inbox); Cluster.Enqueue(inboxKey, dto); var clientsReached = Cluster.PublishToChannel(inboxNotifyKey, dto.MessageId); return(unit); }
public void Dispose() { //tokenSource?.Cancel(); //tokenSource?.Dispose(); //tokenSource = null; userNotify.Cancel(); try { cluster?.UnsubscribeChannel(ActorInboxCommon.ClusterUserInboxNotifyKey(actor.Id)); } catch { }; try { cluster?.UnsubscribeChannel(ActorInboxCommon.ClusterSystemInboxNotifyKey(actor.Id)); } catch { }; cluster = null; }
public Unit Pause() { lock (sync) { if (!IsPaused) { IsPaused = true; cluster?.UnsubscribeChannel(ActorInboxCommon.ClusterUserInboxNotifyKey(actor.Id)); } } return(unit); }
void CheckRemoteInbox(string key, bool pausable) { var inbox = this; var count = cluster?.QueueLength(key) ?? 0; while (count > 0 && (!pausable || !IsPaused)) { var directive = InboxDirective.Default; ActorInboxCommon.GetNextMessage(cluster, actor.Id, key).IfSome( x => iter(x, (dto, msg) => { try { switch (msg.MessageType) { case Message.Type.User: directive = ActorInboxCommon.UserMessageInbox(actor, inbox, (UserControlMessage)msg, parent); break; case Message.Type.UserControl: directive = ActorInboxCommon.UserMessageInbox(actor, inbox, (UserControlMessage)msg, parent); break; } } catch (Exception e) { var session = msg.SessionId == null ? None : Some(new SessionId(msg.SessionId)); ActorContext.System(actor.Id).WithContext(new ActorItem(actor, inbox, actor.Flags), parent, dto.Sender, msg as ActorRequest, msg, session, () => replyErrorIfAsked(e)); tell(ActorContext.System(actor.Id).DeadLetters, DeadLetter.create(dto.Sender, actor.Id, e, "Remote message inbox.", msg)); logSysErr(e); } finally { if ((directive & InboxDirective.Pause) != 0) { IsPaused = true; directive = directive & (~InboxDirective.Pause); } if (directive == InboxDirective.Default) { cluster?.Dequeue <RemoteMessageDTO>(key); } } })); if (directive == InboxDirective.Default) { count--; } } }
void SubscribeToUserInboxChannel() { cluster.UnsubscribeChannel(ActorInboxCommon.ClusterUserInboxNotifyKey(actor.Id)); cluster.SubscribeToChannel <string>(ActorInboxCommon.ClusterUserInboxNotifyKey(actor.Id)).Subscribe( msg => { if (userInbox.Count == 0) { CheckRemoteInbox(ActorInboxCommon.ClusterUserInboxKey(actor.Id), cluster, actor.Id, sysInbox, userInbox, true); } }); cluster.PublishToChannel(ActorInboxCommon.ClusterUserInboxNotifyKey(actor.Id), Guid.NewGuid().ToString()); }
void SetupRemoteSubscriptions(Option <ICluster> cluster, ProcessFlags flags) { if (remoteSubsAcquired) { return; } cluster.IfSome(c => { // Watches for local state-changes and persists them if ((flags & ProcessFlags.PersistState) == ProcessFlags.PersistState) { try { stateSubject.Subscribe(state => c.SetValue(StateKey, state)); } catch (Exception e) { logSysErr(e); } } // Watches for local state-changes and publishes them remotely if ((flags & ProcessFlags.RemoteStatePublish) == ProcessFlags.RemoteStatePublish) { try { stateSubject.Subscribe(state => c.PublishToChannel(ActorInboxCommon.ClusterStatePubSubKey(Id), state)); } catch (Exception e) { logSysErr(e); } } // Watches for publish events and remotely publishes them if ((flags & ProcessFlags.RemotePublish) == ProcessFlags.RemotePublish) { try { publishSubject.Subscribe(msg => c.PublishToChannel(ActorInboxCommon.ClusterPubSubKey(Id), msg)); } catch (Exception e) { logSysErr(e); } } }); remoteSubsAcquired = true; }
/// <summary> /// Clear all settings for the process (or role if outside of the message-loop of a Process) /// </summary> public static Unit clear(SystemName system = default(SystemName)) { if (InMessageLoop) { ActorContext.System(Self) .Settings .ClearSettingsOverride(ActorInboxCommon.ClusterSettingsKey(Self), ActorContext.Request.ProcessFlags); return(unit); } else { return(ActorContext.System(system).Settings.ClearSettingsOverride($"role-{Role.Current.Value}@settings", ProcessFlags.PersistState)); } }
public Either <string, bool> CanAccept <T>() { if (Cluster.Exists(ActorInboxCommon.ClusterMetaDataKey(ProcessId))) { var meta = Cluster.GetValue <ProcessMetaData>(ActorInboxCommon.ClusterMetaDataKey(ProcessId)); return(meta == null ? true : TypeHelper.IsMessageValidForProcess(typeof(T), meta.MsgTypeNames).Map(_ => true)); } else { return(true); } }
/// <summary> /// Write a setting /// </summary> /// <param name="name">Name of the setting</param> /// <param name="prop">If the setting is a complex value (like a map or record), then /// this selects the property of the setting to access</param> /// <param name="value">Value to set</param> public static Unit write(string name, string prop, object value, SystemName system = default(SystemName)) { if (InMessageLoop) { ActorContext.System(Self) .Settings .WriteSettingOverride(ActorInboxCommon.ClusterSettingsKey(Self), value, name, prop, ActorContext.Request.ProcessFlags); return(unit); } else { return(ActorContext.System(system).Settings.WriteSettingOverride($"role-{Role.Current.Value}@settings", value, name, prop, ProcessFlags.PersistState)); } }
void ValidateMessageType(object message, ProcessId sender) { if (Cluster.Exists(ActorInboxCommon.ClusterMetaDataKey(ProcessId))) { var meta = Cluster.GetValue <ProcessMetaData>(ActorInboxCommon.ClusterMetaDataKey(ProcessId)); if (meta == null) { return; } TypeHelper.IsMessageValidForProcess(message, meta.MsgTypeNames).IfLeft((string err) => { throw new ProcessException($"{err} for Process ({ProcessId}).", ProcessId.Path, sender.Path, null); }); } }
public IEnumerable <Type> GetValidMessageTypes() { if (Cluster.Exists(ActorInboxCommon.ClusterMetaDataKey(ProcessId))) { var meta = Cluster.GetValue <ProcessMetaData>(ActorInboxCommon.ClusterMetaDataKey(ProcessId)); if (meta == null) { return(new Type[0]); } return(meta.MsgTypeNames.Map(Type.GetType).ToArray()); } else { return(new Type[0]); } }
public Either <string, bool> HasStateTypeOf <T>() { if (Cluster.Exists(ActorInboxCommon.ClusterMetaDataKey(ProcessId))) { var meta = Cluster.GetValue <ProcessMetaData>(ActorInboxCommon.ClusterMetaDataKey(ProcessId)); if (meta == null) { return(true); } return(TypeHelper.HasStateTypeOf(typeof(T), meta.StateTypeInterfaces)); } else { return(true); } }
public Unit Tell(object message, ProcessId sender) { if (message == null) { throw new ArgumentNullException(nameof(message)); } if (userInbox != null) { try { return(ActorInboxCommon.PreProcessMessage <T>(sender, actor.Id, message).IfSome(msg => userInbox.Post(msg))); } catch (QueueFullException) { throw new ProcessInboxFullException(actor.Id, MailboxSize, "user"); } } return(unit); }
/// <summary> /// TODO: This is a combination of code in ActorCommon.GetNextMessage and /// CheckRemoteInbox. Some factoring is needed. /// </summary> void SysInbox(RemoteMessageDTO dto) { try { if (dto == null) { // Failed to deserialise properly return; } if (dto.Tag == 0 && dto.Type == 0) { // Message is bad tell(ActorContext.System(actor.Id).DeadLetters, DeadLetter.create(dto.Sender, actor.Id, null, "Failed to deserialise message: ", dto)); return; } var msg = MessageSerialiser.DeserialiseMsg(dto, actor.Id); try { lock (sync) { ActorInboxCommon.SystemMessageInbox(actor, this, (SystemMessage)msg, parent); } } catch (Exception e) { var session = msg.SessionId == null ? None : Some(new SessionId(msg.SessionId)); ActorContext.System(actor.Id).WithContext(new ActorItem(actor, this, actor.Flags), parent, dto.Sender, msg as ActorRequest, msg, session, () => replyErrorIfAsked(e)); tell(ActorContext.System(actor.Id).DeadLetters, DeadLetter.create(dto.Sender, actor.Id, e, "Remote message inbox.", msg)); logSysErr(e); } } catch (Exception e) { logSysErr(e); } }
/// <summary> /// Shutdown everything from this node down /// </summary> public Unit Shutdown(bool maintainState) { cancellationTokenSource.Cancel(); // this will signal other operations not to start processing more messages (ProcessMessage) or startup again (Startup), even if they already received something from the queue lock (sync) { if (maintainState == false && Flags != ProcessFlags.Default) { cluster.IfSome(c => { // TODO: Make this transactional // { c.DeleteMany( StateKey, ActorInboxCommon.ClusterUserInboxKey(Id), ActorInboxCommon.ClusterSystemInboxKey(Id), ActorInboxCommon.ClusterMetaDataKey(Id), ActorInboxCommon.ClusterSettingsKey(Id)); sys.DeregisterById(Id); // } sys.Settings.ClearInMemorySettingsOverride(ActorInboxCommon.ClusterSettingsKey(Id)); }); } RemoveAllSubscriptions(); publishSubject.OnCompleted(); stateSubject.OnCompleted(); remoteSubsAcquired = false; strategyState = StrategyState.Empty; state.IfSome(shutdownFn); DisposeState(); sys.DispatchTerminate(Id); return(unit); } }