Beispiel #1
0
 /// <summary>
 /// Get the types of messages that the provided ProcessId accepts.  Returns
 /// an empty list if it can't be resolved for whatever reason (process doesn't
 /// exist/JS process/etc.).
 /// </summary>
 /// <param name="pid">Process ID to query</param>
 /// <returns>List of types</returns>
 public static IEnumerable <Type> validMessageTypes(ProcessId pid) =>
 ActorContext.System(pid).GetDispatcher(pid).GetValidMessageTypes();
Beispiel #2
0
 public Unit DispatchWatch(ProcessId pid)
 {
     sys.GetDispatcher(pid).Watch(Id);
     return(ActorContext.System(Id).AddWatcher(pid, Id));
 }
Beispiel #3
0
        public ProcessId ActorCreate <S, T>(
            ActorItem parent,
            ProcessName name,
            Func <S, T, S> actorFn,
            Func <IActor, S> setupFn,
            Func <S, ProcessId, S> termFn,
            State <StrategyContext, Unit> strategy,
            ProcessFlags flags,
            int maxMailboxSize,
            bool lazy)
        {
            var actor = new Actor <S, T>(cluster, parent, name, actorFn, setupFn, termFn, strategy, flags, ActorContext.System(parent.Actor.Id).Settings, this);

            IActorInbox inbox = null;

            if ((actor.Flags & ProcessFlags.ListenRemoteAndLocal) == ProcessFlags.ListenRemoteAndLocal && cluster.IsSome)
            {
                inbox = new ActorInboxDual <S, T>();
            }
            else if ((actor.Flags & ProcessFlags.PersistInbox) == ProcessFlags.PersistInbox && cluster.IsSome)
            {
                inbox = new ActorInboxRemote <S, T>();
            }
            else
            {
                inbox = new ActorInboxLocal <S, T>();
            }

            var item = new ActorItem(actor, inbox, actor.Flags);

            parent.Actor.LinkChild(item);

            // Auto register if there are config settings and we
            // have the variable name it was assigned to.
            ActorContext.System(actor.Id).Settings.GetProcessRegisteredName(actor.Id).Iter(regName =>
            {
                // Also check if 'dispatch' is specified in the config, if so we will
                // register the Process as a role dispatcher PID instead of just its
                // PID.
                ActorContext.System(actor.Id).Settings.GetProcessDispatch(actor.Id)
                .Match(
                    Some: disp => Process.register(regName, Disp[$"role-{disp}"][Role.Current].Append(actor.Id.Skip(1))),
                    None: () => Process.register(regName, actor.Id)
                    );
            });

            try
            {
                inbox.Startup(actor, parent, cluster, maxMailboxSize);
                if (!lazy)
                {
                    TellSystem(actor.Id, SystemMessage.StartupProcess);
                }
            }
            catch
            {
                item?.Actor?.ShutdownProcess(false);
                throw;
            }
            return(item.Actor.Id);
        }
Beispiel #4
0
 /// <summary>
 /// Send a message to a process
 /// </summary>
 /// <param name="pid">Process ID to send to</param>
 /// <param name="message">Message to send</param>
 /// <param name="sender">Optional sender override.  The sender is handled automatically if you do not provide one.</param>
 public static Unit tell <T>(ProcessId pid, T message, ProcessId sender = default(ProcessId)) =>
 message is UserControlMessage
         ? ActorContext.System(pid).TellUserControl(pid, message as UserControlMessage)
         : ActorContext.System(pid).Tell(pid, message, Schedule.Immediate, sender);
Beispiel #5
0
 /// <summary>
 /// Send a message at a specified time in the future
 /// </summary>
 /// <returns>IDisposable that you can use to cancel the operation if necessary.  You do not need to call Dispose
 /// for any other reason.</returns>
 /// <param name="pid">Process ID to send to</param>
 /// <param name="message">Message to send</param>
 /// <param name="schedule">A structure that defines the method of delivery of the scheduled message</param>
 /// <param name="sender">Optional sender override.  The sender is handled automatically if you do not provide one.</param>
 public static Unit tell <T>(ProcessId pid, T message, Schedule schedule, ProcessId sender = default(ProcessId)) =>
 ActorContext.System(pid).Tell(pid, message, schedule, sender);
 /// <summary>
 /// Register a named process (a kind of DNS for Processes).
 ///
 /// If the Process is visible to the cluster (PersistInbox) then the
 /// registration becomes a permanent named look-up until Process.deregister
 /// is called.
 ///
 /// See remarks.
 /// </summary>
 /// <remarks>
 /// Multiple Processes can register under the same name.  You may use
 /// a dispatcher to work on them collectively (wherever they are in the
 /// cluster).  i.e.
 ///
 ///     var regd = register("proc", pid);
 ///     tell(Dispatch.Broadcast[regd], "Hello");
 ///     tell(Dispatch.First[regd], "Hello");
 ///     tell(Dispatch.LeastBusy[regd], "Hello");
 ///     tell(Dispatch.Random[regd], "Hello");
 ///     tell(Dispatch.RoundRobin[regd], "Hello");
 ///
 /// </remarks>
 /// <param name="name">Name to register under</param>
 /// <param name="process">Process to be registered</param>
 /// <returns>A ProcessId that allows dispatching to the process(es).  The result
 /// would look like /disp/reg/name</returns>
 public static ProcessId register(ProcessName name, ProcessId process) =>
 ActorContext.System(process).Register($"{Role.Current.Value}-{name.Value}", process);
 /// <summary>
 /// Deregister all Processes associated with a name. NOTE: Be very careful
 /// with usage of this function if you didn't handle the registration you
 /// are potentially disconnecting many Processes from their registered name.
 ///
 /// See remarks.
 /// </summary>
 /// <remarks>
 /// Any Process (or dispatcher, or role, etc.) can be registered by a name -
 /// a kind of DNS for ProcessIds.  There can be multiple names associated
 /// with a single ProcessId and multiple ProcessIds associated with a name.
 ///
 /// This function removes all registered ProcessIds for a specific name.
 /// If you wish to deregister all names registered for specific Process then
 /// use Process.deregisterById(pid)
 /// </remarks>
 /// <param name="name">Name of the process to deregister</param>
 public static Unit deregisterByName(ProcessName name, SystemName system = default(SystemName)) =>
 ActorContext.System(system).DeregisterByName($"{Role.Current.Value}-{name.Value}");
Beispiel #8
0
 /// <summary>
 /// Finds all *persistent* registered names in a role
 /// </summary>
 /// <param name="role">Role to limit search to</param>
 /// <param name="keyQuery">Key query.  * is a wildcard</param>
 /// <returns>Registered names</returns>
 public static IEnumerable <ProcessName> queryRegistered(ProcessName role, string keyQuery, SystemName system = default(SystemName)) =>
 ActorContext.System(system).Cluster
 .Map(c => c.QueryRegistered(role.Value, keyQuery))
 .IfNone(List.empty <ProcessName>());
Beispiel #9
0
 /// <summary>
 /// Finds all *persistent* processes based on the search pattern provided.  Note the returned
 /// ProcessIds may contain processes that aren't currently active.  You can still post
 /// to them however.
 /// </summary>
 /// <param name="keyQuery">Key query.  * is a wildcard</param>
 /// <returns>Matching ProcessIds</returns>
 public static IEnumerable <ProcessId> queryProcesses(string keyQuery, SystemName system = default(SystemName)) =>
 ActorContext.System(system).Cluster
 .Map(c => c.QueryProcesses(keyQuery))
 .IfNone(new ProcessId[0]);
Beispiel #10
0
 public override Unit Run(ProcessId pid) =>
 ActorContext.System(pid).Settings.ClearSettingsOverride(ActorInboxCommon.ClusterSettingsKey(pid), Flags);
Beispiel #11
0
 public static ProcessOpTransaction Start(ProcessId pid) =>
 new ProcessOpTransaction(pid, Que <ProcessOp> .Empty, ActorContext.System(pid).Settings.GetProcessSettingsOverrides(pid));
Beispiel #12
0
 public override Unit Run(ProcessId pid) =>
 ActorContext.System(pid).Settings.WriteSettingOverride(ActorInboxCommon.ClusterSettingsKey(pid), Value, Name, Prop, Flags);
Beispiel #13
0
        public static InboxDirective SystemMessageInbox <S, T>(Actor <S, T> actor, IActorInbox inbox, SystemMessage msg, ActorItem parent)
        {
            var session = msg.SessionId == null
                ? None
                : Some(new SessionId(msg.SessionId));

            return(ActorContext.System(actor.Id).WithContext(new ActorItem(actor, inbox, actor.Flags), parent, ProcessId.NoSender, null, msg, session, () =>
            {
                switch (msg.Tag)
                {
                case Message.TagSpec.Restart:
                    actor.Restart(inbox.IsPaused);
                    break;

                case Message.TagSpec.LinkChild:
                    var lc = msg as SystemLinkChildMessage;
                    actor.LinkChild(lc.Child);
                    break;

                case Message.TagSpec.UnlinkChild:
                    var ulc = (msg as SystemUnLinkChildMessage).SetSystem(actor.Id.System);
                    actor.UnlinkChild(ulc.Child);
                    break;

                case Message.TagSpec.ChildFaulted:
                    var cf = (msg as SystemChildFaultedMessage).SetSystem(actor.Id.System);
                    return actor.ChildFaulted(cf.Child, cf.Sender, cf.Exception, cf.Message);

                case Message.TagSpec.StartupProcess:
                    var startupProcess = msg as StartupProcessMessage;
                    var inboxDirective = actor.Startup();     // get feedback whether startup will somehow trigger Unpause itself (i.e. error => strategy => restart)
                    if (startupProcess.UnpauseAfterStartup && !inboxDirective.HasFlag(InboxDirective.Pause))
                    {
                        inbox.Unpause();
                    }
                    break;

                case Message.TagSpec.ShutdownProcess:
                    var shutdownProcess = msg as ShutdownProcessMessage;
                    actor.ShutdownProcess(shutdownProcess.MaintainState);
                    break;

                case Message.TagSpec.Unpause:
                    inbox.Unpause();
                    break;

                case Message.TagSpec.Pause:
                    inbox.Pause();
                    break;     // do not return InboxDirective.Pause because system queue should never pause

                case Message.TagSpec.Watch:
                    var awm = msg as SystemAddWatcherMessage;
                    actor.AddWatcher(awm.Id);
                    break;

                case Message.TagSpec.UnWatch:
                    var rwm = msg as SystemRemoveWatcherMessage;
                    actor.RemoveWatcher(rwm.Id);
                    break;

                case Message.TagSpec.DispatchWatch:
                    var dwm = msg as SystemDispatchWatchMessage;
                    actor.DispatchWatch(dwm.Id);
                    break;

                case Message.TagSpec.DispatchUnWatch:
                    var duwm = msg as SystemDispatchUnWatchMessage;
                    actor.DispatchUnWatch(duwm.Id);
                    break;
                }
                return InboxDirective.Default;
            }));
        }
Beispiel #14
0
        public static Tuple <long, Dictionary <long, AskActorReq> > Inbox(Tuple <long, Dictionary <long, AskActorReq> > state, object msg)
        {
            var reqId = state.Item1;
            var dict  = state.Item2;

            if (msg is AskActorReq)
            {
                reqId++;

                var req = (AskActorReq)msg;
                ActorContext.System(req.To).Ask(req.To, new ActorRequest(req.Message, req.To, Self, reqId), Self);
                dict.Add(reqId, req);
            }
            else
            {
                var res = (ActorResponse)msg;
                if (dict.ContainsKey(res.RequestId))
                {
                    var req = dict[res.RequestId];
                    try
                    {
                        if (res.IsFaulted)
                        {
                            Exception ex = null;

                            // Let's be reeeally safe here and do everything we can to get some valid information out of
                            // the response to report to the process doing the 'ask'.
                            try
                            {
                                var msgtype = req.ReplyType;
                                if (msgtype == res.Message.GetType() && typeof(Exception).GetTypeInfo().IsAssignableFrom(msgtype.GetTypeInfo()))
                                {
                                    // Type is fine, just return it as an error
                                    ex = (Exception)res.Message;
                                }
                                else
                                {
                                    if (res.Message is string)
                                    {
                                        ex = (Exception)Deserialise.Object(res.Message.ToString(), msgtype);
                                    }
                                    else
                                    {
                                        ex = (Exception)Deserialise.Object(JsonConvert.SerializeObject(res.Message), msgtype);
                                    }
                                }
                            }
                            catch
                            {
                                ex = new Exception(res.Message?.ToString() ?? $"An unknown error was thrown by {req.To}");
                            }

                            req.Complete(new AskActorRes(new ProcessException($"Process issue: {ex.Message}", req.To.Path, req.ReplyTo.Path, ex), req.ReplyType));
                        }
                        else
                        {
                            req.Complete(new AskActorRes(res.Message, req.ReplyType));
                        }
                    }
                    catch (Exception e)
                    {
                        req.Complete(new AskActorRes(new ProcessException($"Process issue: {e.Message}", req.To.Path, req.ReplyTo.Path, e), req.ReplyType));
                        logSysErr(e);
                    }
                    finally
                    {
                        dict.Remove(res.RequestId);
                    }
                }
                else
                {
                    logWarn($"Request ID doesn't exist: {res.RequestId}");
                }
            }

            return(new Tuple <long, Dictionary <long, AskActorReq> >(reqId, dict));
        }
Beispiel #15
0
 private IEnumerable <IActorDispatch> GetWorkers() =>
 group.Map(pid => ActorContext.System(pid).GetDispatcher(pid));
Beispiel #16
0
 /// <summary>
 /// Finds all *persistent* processes based on the search pattern provided and then returns the
 /// meta-data associated with them.
 /// </summary>
 /// <param name="keyQuery">Key query.  * is a wildcard</param>
 /// <returns>Map of ProcessId to ProcessMetaData</returns>
 public static HashMap <ProcessId, ProcessMetaData> queryProcessMetaData(string keyQuery, SystemName system = default(SystemName)) =>
 ActorContext.System(system).Cluster
 .Map(c => c.QueryProcessMetaData(keyQuery))
 .IfNone(HashMap.empty <ProcessId, ProcessMetaData>());
 /// <summary>
 /// Register a named process (a kind of DNS for Processes).
 ///
 /// If the Process is visible to the cluster (PersistInbox) then the
 /// registration becomes a permanent named look-up until Process.deregister
 /// is called.
 ///
 /// See remarks.
 /// </summary>
 /// <remarks>
 /// Multiple Processes can register under the same name.  You may use
 /// a dispatcher to work on them collectively (wherever they are in the
 /// cluster).  i.e.
 ///
 ///     var regd = register("proc");
 ///     tell(Dispatch.Broadcast[regd], "Hello");
 ///     tell(Dispatch.First[regd], "Hello");
 ///     tell(Dispatch.LeastBusy[regd], "Hello");
 ///     tell(Dispatch.Random[regd], "Hello");
 ///     tell(Dispatch.RoundRobin[regd], "Hello");
 ///
 ///     This should be used from within a process' message loop only
 /// </remarks>
 /// <param name="name">Name to register under</param>
 /// <returns>A ProcessId that allows dispatching to the process via the name.  The result
 /// would look like /disp/reg/name</returns>
 public static ProcessId register(ProcessName name, SystemName system = default(SystemName)) =>
 InMessageLoop
         ? ActorContext.System(system).Register($"{Role.Current.Value}-{name.Value}", Self)
         : raiseUseInMsgLoopOnlyException <ProcessId>(nameof(name));
Beispiel #18
0
        private static void StartFromConfig(ProcessSystemConfig config)
        {
            lock (sync)
            {
                InitLocalScheduler();

                config.Cluster.Match(
                    Some: _ =>
                {
                    // Extract cluster settings
                    var provider    = config.GetClusterSetting("provider", "value", "redis");
                    var role        = config.GetClusterSetting("role", "value", name => clusterSettingMissing <string>(name));
                    var clusterConn = config.GetClusterSetting("connection", "value", "localhost");
                    var clusterDb   = config.GetClusterSetting("database", "value", "0");
                    var env         = config.SystemName;
                    var userEnv     = config.GetClusterSetting <string>("user-env", "value");

                    var appProfile = new AppProfile(
                        config.NodeName,
                        role,
                        clusterConn,
                        clusterDb,
                        env,
                        userEnv
                        );

                    // Look for an existing actor-system with the same system name
                    var current = ActorContext.Systems.Filter(c => c.Value == env).HeadOrNone();

                    // Work out if something significant has changed that will cause us to restart
                    var restart = current.Map(ActorContext.System)
                                  .Map(c => c.AppProfile.NodeName != appProfile.NodeName ||
                                       c.AppProfile.Role != appProfile.Role ||
                                       c.AppProfile.ClusterConn != appProfile.ClusterConn ||
                                       c.AppProfile.ClusterDb != appProfile.ClusterDb);

                    // Either restart / update settings / or start new
                    restart.Match(
                        Some: r =>
                    {
                        if (r)
                        {
                            // Restart
                            try
                            {
                                ActorContext.StopSystem(env);
                            }
                            catch (Exception e)
                            {
                                logErr(e);
                            }
                            StartFromConfig(config);
                        }
                        else
                        {
                            // Update settings
                            ActorContext.System(env).UpdateSettings(config, appProfile);
                            var cluster = from systm in current.Map(ActorContext.System)
                                          from clstr in systm.Cluster
                                          select clstr;
                        }
                    },
                        None: () =>
                    {
                        // Start new
                        ICluster cluster = Cluster.connect(
                            provider,
                            config.NodeName,
                            clusterConn,
                            clusterDb,
                            role
                            );

                        ActorContext.StartSystem(env, Optional(cluster), appProfile, config);
                        config.PostConnect();
                    });
                },
                    None: () =>
                {
                    ActorContext.StartSystem(new SystemName(""), None, AppProfile.NonClustered, config);
                });
            }
        }
 /// <summary>
 /// Deregister a Process from any names it's been registered as.
 ///
 /// See remarks.
 /// </summary>
 /// <remarks>
 /// Any Process (or dispatcher, or role, etc.) can be registered by a name -
 /// a kind of DNS for ProcessIds.  There can be multiple names associated
 /// with a single ProcessId.
 ///
 /// This function removes all registered names for a specific ProcessId.
 /// If you wish to deregister all ProcessIds registered under a name then
 /// use Process.deregisterByName(name)
 /// </remarks>
 /// <param name="process">Process to be deregistered</param>
 public static Unit deregisterById(ProcessId process) =>
 ActorContext.System(process).DeregisterById(process);
Beispiel #20
0
 /// <summary>
 /// Access a setting
 /// If in a Process message loop, then this accesses the configuration settings
 /// for the Process from the the configuration file, or stored in the cluster.
 /// If not in a Process message loop, then this accesses 'global' configuration
 /// settings.
 /// </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>
 /// <returns>Optional configuration setting value</returns>
 public static T read <T>(string name, string prop, T defaultValue, SystemName system = default(SystemName)) =>
 InMessageLoop
         ? ActorContext.System(Self).Settings.GetProcessSetting(Self, name, prop, defaultValue, ActorContext.Request.ProcessFlags)
         : ActorContext.System(system).Settings.GetRoleSetting(name, prop, defaultValue);
 /// <summary>
 /// Find a process by its *registered* name (a kind of DNS for Processes) in the
 /// role specified.
 ///
 /// See remarks.
 /// </summary>
 /// <remarks>
 /// Multiple Processes can register under the same name.  You may use
 /// a dispatcher to work on them collectively (wherever they are in the
 /// cluster).  i.e.
 ///
 ///     var regd = register("proc", pid);
 ///     tell(Dispatch.Broadcast[regd], "Hello");
 ///     tell(Dispatch.First[regd], "Hello");
 ///     tell(Dispatch.LeastBusy[regd], "Hello");
 ///     tell(Dispatch.Random[regd], "Hello");
 ///     tell(Dispatch.RoundRobin[regd], "Hello");
 ///
 /// </remarks>
 /// <param name="role">Process role</param>
 /// <param name="name">Process name</param>
 /// <returns>A ProcessId that allows dispatching to the process(es).  The result
 /// would look like /disp/reg/name</returns>
 public static ProcessId find(ProcessName role, ProcessName name) =>
 ActorContext.System(default(SystemName)).Disp["reg"][$"{role.Value}-{name.Value}"];
Beispiel #22
0
 /// <summary>
 /// Starts a new session in the Process system with the specified
 /// session ID
 /// </summary>
 /// <param name="sid">Session ID</param>
 /// <param name="timeout">Session timeout</param>
 public static SessionId sessionStart(SessionId sid, Time timeout, SystemName system)
 {
     ActorContext.System(system).Sessions.Start(sid, (int)(timeout / 1.Seconds()));
     ActorContext.SessionId = sid;
     return(sid);
 }
Beispiel #23
0
 /// <summary>
 /// Send a message to a process
 /// </summary>
 /// <param name="pid">Process ID to send to</param>
 /// <param name="message">Message to send</param>
 /// <param name="sender">Optional sender override.  The sender is handled automatically if you do not provide one.</param>
 internal static Unit tellSystem <T>(ProcessId pid, T message, ProcessId sender = default(ProcessId)) =>
 ActorContext.System(pid).TellSystem(pid, message as SystemMessage);
Beispiel #24
0
 /// <summary>
 /// Ask a process for a reply
 /// </summary>
 /// <param name="pid">Process to ask</param>
 /// <param name="message">Message to send</param>
 /// <param name="sender">Sender process</param>
 /// <returns>The response to the request</returns>
 public static T ask <T>(ProcessId pid, object message, ProcessId sender) =>
 ActorContext.System(pid).Ask <T>(pid, message, sender);
Beispiel #25
0
 /// <summary>
 /// Send a message at a specified time in the future
 /// </summary>
 /// <remarks>
 /// It is advised to use the variant that takes a TimeSpan, this will fail to be accurate across a Daylight Saving
 /// Time boundary or if you use non-UTC dates
 /// </remarks>
 /// <returns>IDisposable that you can use to cancel the operation if necessary.  You do not need to call Dispose
 /// for any other reason.</returns>
 /// <param name="pid">Process ID to send to</param>
 /// <param name="message">Message to send</param>
 /// <param name="delayUntil">Date and time to send</param>
 /// <param name="sender">Optional sender override.  The sender is handled automatically if you do not provide one.</param>
 public static Unit tell <T>(ProcessId pid, T message, DateTime delayUntil, ProcessId sender = default(ProcessId)) =>
 ActorContext.System(pid).Tell(pid, message, Schedule.Ephemeral(delayUntil), sender);
Beispiel #26
0
 /// <summary>
 /// Ask a process for a reply (if the process is running).  If the process isn't running
 /// then None is returned
 /// </summary>
 /// <param name="pid">Process to ask</param>
 /// <param name="message">Message to send</param>
 /// <param name="sender">Sender process</param>
 /// <returns>The response to the request or None if the process isn't running</returns>
 public static Option <T> askIfAlive <T>(ProcessId pid, object message, ProcessId sender) =>
 ping(pid)
         ? Optional(ActorContext.System(pid).Ask <T>(pid, message, sender))
         : None;
Beispiel #27
0
 public Unit DispatchUnWatch(ProcessId pid)
 {
     sys.GetDispatcher(pid).UnWatch(Id);
     return(ActorContext.System(Id).RemoveWatcher(pid, Id));
 }
Beispiel #28
0
 /// <summary>
 /// Ask children the same message
 /// </summary>
 /// <param name="message">Message to send</param>
 /// <returns></returns>
 public static IEnumerable <T> askChildren <T>(object message, int take = Int32.MaxValue) =>
 ActorContext.System(default(SystemName)).AskMany <T>(Children.Values, message, take);
 /// <summary>
 /// Not advised to use this directly, but allows access to the underlying data-store.
 /// </summary>
 public static Option <ICluster> SystemCluster(SystemName system = default(SystemName)) =>
 ActorContext.System(system).Cluster;
Beispiel #30
0
 /// <summary>
 /// Resolves a ProcessId into the absolute ProcessIds that it represents
 /// This allows live resolution of role-based ProcessIds to their real node
 /// ProcessIds.
 /// </summary>
 /// <remarks>Mostly useful for debugging, but could be useful for layering
 /// additional logic to any message dispatch.
 /// </remarks>
 /// <param name="pid"></param>
 /// <returns>Enumerable of resolved ProcessIds - could be zero length</returns>
 public static IEnumerable <ProcessId> resolve(ProcessId pid) =>
 ActorContext.System(pid).ResolveProcessIdSelection(pid);