/// <summary>
        /// Create a new process by name.
        /// If this is called from within a process' message loop
        /// then the new process will be a child of the current process.  If it is called from
        /// outside of a process, then it will be made a child of the root 'user' process.
        /// </summary>
        /// <typeparam name="T">Type of messages that the child-process can accept</typeparam>
        /// <param name="Name">Name of the child-process</param>
        /// <param name="Setup">Startup and restart function</param>
        /// <param name="Inbox">Function that is the process</param>
        /// <param name="Flags">Process flags</param>
        /// <param name="Strategy">Failure supervision strategy</param>
        /// <param name="Terminated">Message function to call when a Process [that this Process
        /// watches] terminates</param>
        /// <returns>A ProcessId that identifies the child</returns>
        public static ProcessId spawn <S, T>(
            ProcessName Name,
            Func <S> Setup,
            Func <S, T, S> Inbox,
            ProcessFlags Flags = ProcessFlags.Default,
            State <StrategyContext, Unit> Strategy = null,
            int MaxMailboxSize = ProcessSetting.DefaultMailboxSize,
            Func <S, ProcessId, S> Terminated = null,
            SystemName System = default(SystemName)
            )
        {
            if (System.IsValid && ActorContext.Request != null)
            {
                throw new ProcessException("When spawning you can only specify a System from outside of a Process", ActorContext.Self[Name].Path, "");
            }

            var sys = System.IsValid
                ? ActorContext.System(System)
                : ActorContext.DefaultSystem;

            var parent = System.IsValid
                ? sys.UserContext.Self
                : ActorContext.SelfProcess;

            return(sys.ActorCreate(parent, Name, Inbox, Setup, Terminated, Strategy, Flags, MaxMailboxSize, false));
        }
        public ActorSystemBootstrap(IActorSystem system, Option <ICluster> cluster, ProcessId rootId, IActor rootProcess, IActorInbox rootInbox, ProcessName rootProcessName, ActorSystemConfig config, ProcessSystemConfig settings, SessionSync sync)
        {
            System = system;
            Sync   = sync;

            var parent = new ActorItem(new NullProcess(system.Name), new NullInbox(), ProcessFlags.Default);

            rootProcess = new Actor <ActorSystemBootstrap, Unit>(
                cluster,
                parent,
                rootProcessName,
                SystemInbox,
                _ => this,
                null,
                Process.DefaultStrategy,
                ProcessFlags.Default,
                settings,
                system
                );

            root            = new ActorItem(rootProcess, rootInbox, rootProcess.Flags);
            Config          = config;
            Settings        = settings;
            Cluster         = cluster;
            RootProcess     = rootProcess;
            RootInbox       = rootInbox;
            RootProcessName = rootProcessName;
        }
        /// <summary>
        /// Spawns a new process with that routes each message to the Workers
        /// in a round robin fashion.
        /// </summary>
        /// <typeparam name="S">State type</typeparam>
        /// <typeparam name="T">Message type</typeparam>
        /// <param name="Name">Delegator process name</param>
        /// <param name="Flags">Process flags</param>
        /// <returns>Process ID of the delegator process</returns>
        public static ProcessId broadcast <T>(
            ProcessName Name,
            IEnumerable <ProcessId> Workers,
            RouterOption Options = RouterOption.Default,
            ProcessFlags Flags   = ProcessFlags.Default,
            int MaxMailboxSize   = ProcessSetting.DefaultMailboxSize
            )
        {
            if (Workers == null)
            {
                throw new ArgumentNullException(nameof(Workers));
            }
            var workers = Set.createRange(Workers);

            if (workers.Count < 1)
            {
                throw new ArgumentException($"{nameof(Workers)} should have a length of at least 1");
            }

            var router = spawn <T>(
                Name,
                msg => workers.Iter(pid => fwd(pid)),
                Flags,
                DefaultStrategy,
                MaxMailboxSize,
                Terminated: pid => workers = workers.Remove(pid)
                );

            return(WatchWorkers(router, workers, Options));
        }
示例#4
0
        public static Unit DeregisterByName(ProcessName name)
        {
            Cluster.Match(
                Some: c =>
            {
                RemoveLocalRegisteredByName(name);
                var regpath = (ProcessId.Top["__registered"][name]).Path;

                // TODO - Make this transactional
                // {
                var pids = c.GetSet <string>(regpath);
                pids.Iter(pid =>
                {
                    c.SetRemove(pid + "-registered", name.Value);
                });
                c.Delete(regpath);
                // }
            },
                None: () =>
            {
                RemoveLocalRegisteredByName(name);
            }
                );
            return(unit);
        }
示例#5
0
        public static ProcessId Register(ProcessName name, ProcessId pid)
        {
            if (!pid.IsValid)
            {
                throw new InvalidProcessIdException();
            }

            Cluster.Match(
                Some: c =>
            {
                if (IsLocal(pid) && GetDispatcher(pid).IsLocal)
                {
                    AddLocalRegistered(name, pid);
                }
                else
                {
                    // TODO - Make this transactional
                    // {
                    c.SetAddOrUpdate(ProcessId.Top["__registered"][name].Path, pid.Path);
                    c.SetAddOrUpdate(pid.Path + "-registered", name.Value);
                    // }
                }
            },
                None: () => AddLocalRegistered(name, pid)
                );
            return(Disp["reg"][name]);
        }
示例#6
0
        /// <summary>
        /// Spawns a new process with that routes each message by calling GetHashCode on the
        /// message and modding by the number of workers and using that as the worker index
        /// to route to.
        /// </summary>
        /// <typeparam name="S">State type</typeparam>
        /// <typeparam name="T">Message type</typeparam>
        /// <param name="Name">Delegator process name</param>
        /// <param name="Count">Number of worker processes</param>
        /// <param name="Inbox">Worker message handler</param>
        /// <param name="Flags">Process flags</param>
        /// <param name="Strategy">Failure supervision strategy</param>
        /// <returns>Process ID of the delegator process</returns>
        public static ProcessId hash <T>(
            ProcessName Name,
            IEnumerable <ProcessId> Workers,
            Func <int, T, int> HashFunc = null,
            RouterOption Options        = RouterOption.Default,
            ProcessFlags Flags          = ProcessFlags.Default,
            int MaxMailboxSize          = ProcessSetting.DefaultMailboxSize
            )
        {
            if (Workers == null)
            {
                throw new ArgumentNullException(nameof(Workers));
            }
            var workers = Workers.ToArray();

            if (workers.Length < 1)
            {
                throw new ArgumentException($"{nameof(Workers)} should have a length of at least 1");
            }
            HashFunc = HashFunc ?? DefaultHashFunction <T>();
            var router = spawn <T>(
                Name,
                msg =>
            {
                fwd(workers[HashFunc(Children.Count, msg)]);
            },
                Flags,
                DefaultStrategy,
                MaxMailboxSize,
                Terminated: pid => { workers = workers.Where(x => x != pid).ToArray(); }
                );

            return(WatchWorkers(router, workers, Options));
        }
示例#7
0
        /// <summary>
        /// Spawns a new process with that routes each message to the Workers
        /// randomly.
        /// </summary>
        /// <typeparam name="S">State type</typeparam>
        /// <typeparam name="T">Message type</typeparam>
        /// <param name="Name">Delegator process name</param>
        /// <param name="Count">Number of worker processes</param>
        /// <param name="Inbox">Worker message handler</param>
        /// <param name="Flags">Process flags</param>
        /// <param name="Strategy">Failure supervision strategy</param>
        /// <returns>Process ID of the delegator process</returns>
        public static ProcessId random <T>(
            ProcessName Name,
            IEnumerable <ProcessId> Workers,
            RouterOption Options = RouterOption.Default,
            ProcessFlags Flags   = ProcessFlags.Default,
            int MaxMailboxSize   = ProcessSetting.DefaultMailboxSize
            )
        {
            if (Workers == null)
            {
                throw new ArgumentNullException(nameof(Workers));
            }
            var workers = Workers.ToArray();

            if (workers.Length < 1)
            {
                throw new ArgumentException($"{nameof(Workers)} should have a length of at least 1");
            }
            var router = spawn <T>(
                Name,
                msg => fwd(workers[Prelude.random(workers.Length)]),
                Flags,
                DefaultStrategy,
                MaxMailboxSize,
                Terminated: pid => workers = workers.Where(x => x != pid).ToArray()
                );

            return(WatchWorkers(router, workers, Options));
        }
示例#8
0
        internal Actor(
            Option <ICluster> cluster,
            ActorItem parent,
            ProcessName name,
            Func <S, T, S> actor,
            Func <IActor, S> setup,
            Func <S, ProcessId, S> term,
            State <StrategyContext, Unit> strategy,
            ProcessFlags flags
            )
        {
            if (setup == null)
            {
                throw new ArgumentNullException(nameof(setup));
            }
            if (actor == null)
            {
                throw new ArgumentNullException(nameof(actor));
            }

            this.cluster = cluster;
            this.flags   = flags;
            actorFn      = actor;
            termFn       = term;
            setupFn      = setup;
            Parent       = parent;
            Name         = name;
            Strategy     = strategy ?? Process.DefaultStrategy;
            Id           = parent.Actor.Id[name];
            SetupRemoteSubscriptions(cluster, flags);
        }
 ProcessId(ProcessName[] parts, SystemName system, ProcessName name, string path)
 {
     this.parts  = parts;
     this.name   = name;
     this.Path   = path;
     this.System = system;
 }
        /// <summary>
        /// Spawns a new process with that routes each message to the Workers
        /// in a round robin fashion.
        /// </summary>
        /// <typeparam name="S">State type</typeparam>
        /// <typeparam name="T">Message type</typeparam>
        /// <param name="Name">Delegator process name</param>
        /// <param name="Count">Number of worker processes</param>
        /// <param name="Inbox">Worker message handler</param>
        /// <param name="Flags">Process flags</param>
        /// <param name="Strategy">Failure supervision strategy</param>
        /// <returns>Process ID of the delegator process</returns>
        public static ProcessId roundRobin <T>(
            ProcessName Name,
            IEnumerable <ProcessId> Workers,
            RouterOption Options = RouterOption.Default,
            ProcessFlags Flags   = ProcessFlags.Default,
            int MaxMailboxSize   = ProcessSetting.DefaultMailboxSize
            )
        {
            if (Workers == null)
            {
                throw new ArgumentNullException(nameof(Workers));
            }
            var workers = Workers.ToArray();

            if (workers.Length < 1)
            {
                throw new ArgumentException($"{nameof(Workers)} should have a length of at least 1");
            }
            var router = spawn <int, T>(
                Name,
                () => 0,
                (index, msg) =>
            {
                index = index % workers.Length;
                fwd(workers[index]);
                return(index + 1);
            },
                Flags,
                DefaultStrategy,
                MaxMailboxSize,
                Terminated: (index, pid) => { workers = workers.Where(x => x != pid).ToArray(); return(index); }
                );

            return(WatchWorkers(router, workers, Options));
        }
        /// <summary>
        /// Initialise without a config file or text
        /// </summary>
        /// <param name="systemName">Name of the system - this is most useful</param>
        /// <param name="roleName"></param>
        /// <param name="nodeName"></param>
        /// <param name="providerName"></param>
        /// <param name="connectionString"></param>
        /// <param name="catalogueName"></param>
        /// <returns></returns>
        public static Unit initialise(
            SystemName systemName,
            ProcessName roleName,
            ProcessName nodeName,
            string connectionString,
            string catalogueName,
            string providerName = "redis"
            )
        {
            lock (sync)
            {
                var types = new Types();

                StartFromConfig(new ProcessSystemConfig(
                                    systemName,
                                    nodeName.Value,
                                    Map.empty <string, ValueToken>(),
                                    Map.empty <ProcessId, ProcessToken>(),
                                    Map.empty <string, State <StrategyContext, Unit> >(),
                                    new ClusterToken(
                                        None,
                                        List.create(
                                            new NamedValueToken("node-name", new ValueToken(types.String, nodeName.Value), None),
                                            new NamedValueToken("role", new ValueToken(types.String, roleName.Value), None),
                                            new NamedValueToken("env", new ValueToken(types.String, systemName.Value), None),
                                            new NamedValueToken("connection", new ValueToken(types.String, connectionString), None),
                                            new NamedValueToken("database", new ValueToken(types.String, catalogueName), None),
                                            new NamedValueToken("provider", new ValueToken(types.String, providerName), None))),
                                    types
                                    ));
            }
            return(unit);
        }
示例#12
0
        public ProcessId(string path)
        {
            var res = TryParse(path).IfLeft(ex => raise <ProcessId>(ex));

            parts = res.parts;
            name  = res.name;
            Path  = res.Path;
        }
示例#13
0
 public static Unit RemoveDispatcher(ProcessName name)
 {
     lock (sync)
     {
         dispatchers = dispatchers.Remove(name);
     }
     return(unit);
 }
示例#14
0
 /// <summary>
 /// Registers a dispatcher for a role
 /// Dispatchers take in a 'leaf' ProcessId (i.e. /user/my-process) and return an enumerable
 /// of real ProcessIds that the Process system will use to deliver all of the standard functions
 /// like tell, ask, subscribe, etc.
 /// </summary>
 /// <param name="name">Name of the dispatcher</param>
 /// <param name="selector">Function that will be invoked every time a dispatcher based ProcessId
 /// is used.</param>
 /// <returns>A root dispatcher ProcessId.  Use this to create new ProcessIds that will
 /// be passed to the selector function whenever the dispatcher based ProcessId is used</returns>
 public static ProcessId register(ProcessName name, Func<ProcessId, IEnumerable<ProcessId>> selector)
 {
     lock (sync)
     {
         dispatchers = dispatchers.AddOrUpdate(name, selector);
     }
     return ProcessId.Top["disp"][name];
 }
示例#15
0
 public static ProcessId AddDispatcher(ProcessName name, Func <ProcessId, IEnumerable <ProcessId> > selector)
 {
     lock (sync)
     {
         dispatchers = dispatchers.AddOrUpdate(name, selector);
     }
     return(ProcessId.Top["disp"][name]);
 }
示例#16
0
 /// <summary>
 /// Removes the dispatcher registration for the named dispatcher
 /// </summary>
 /// <param name="name">Name of the dispatcher to remove</param>
 public static Unit deregister(ProcessName name)
 {
     lock (sync)
     {
         dispatchers = dispatchers.Remove(name);
     }
     return unit;
 }
        /// <summary>
        /// Spawns a new process with Count worker processes, each message is mapped
        /// and sent to the least busy worker
        /// </summary>
        /// <typeparam name="S">State type</typeparam>
        /// <typeparam name="T">Message type</typeparam>
        /// <param name="Name">Delegator process name</param>
        /// <param name="Count">Number of worker processes</param>
        /// <param name="Inbox">Worker message handler</param>
        /// <param name="Flags">Process flags</param>
        /// <param name="Strategy">Failure supervision strategy</param>
        /// <returns>Process ID of the delegator process</returns>
        public static ProcessId leastBusyMap <S, T, U>(
            ProcessName Name,
            int Count,
            Func <S> Setup,
            Func <S, U, S> Inbox,
            Func <T, U> Map,
            ProcessFlags Flags = ProcessFlags.Default,
            State <StrategyContext, Unit> Strategy = null,
            int MaxMailboxSize = ProcessSetting.DefaultMailboxSize,
            string WorkerName  = "worker"
            )
        {
            if (Inbox == null)
            {
                throw new ArgumentNullException(nameof(Inbox));
            }
            if (Setup == null)
            {
                throw new ArgumentNullException(nameof(Setup));
            }
            if (Count < 1)
            {
                throw new ArgumentException($"{nameof(Count)} should be greater than 0");
            }

            return(spawn <Unit, T>(
                       Name,
                       () =>
            {
                spawnMany(Count, WorkerName, Setup, Inbox, Flags);
                return unit;
            },
                       (_, msg) =>
            {
                var umsg = Map(msg);

                var disps = (from child in Children.Map(c => Tuple(c, ActorContext.GetDispatcher(c))).Values
                             let count = child.Item2.GetInboxCount()
                                         where count >= 0
                                         orderby count
                                         select child)
                            .ToList();

                if (disps.Count == 0)
                {
                    throw new NoRouterWorkersException();
                }
                else
                {
                    fwd(disps.First().Item1, umsg);
                }
                return unit;
            },
                       Flags,
                       Strategy,
                       MaxMailboxSize
                       ));
        }
示例#18
0
        /// <summary>
        /// Spawns a new process with N worker processes, each message is mapped
        /// from T to IEnumerable U before each resulting U is passed to the worker
        /// processes randomly.
        /// </summary>
        /// <typeparam name="T">Message type</typeparam>
        /// <typeparam name="U">Mapped message type</typeparam>
        /// <param name="Name">Delegator process name</param>
        /// <param name="Count">Number of worker processes</param>
        /// <param name="map">Maps the message from T to IEnumerable U before each one is passed to the workers</param>
        /// <param name="Inbox">Worker message handler</param>
        /// <param name="Flags">Process flags</param>
        /// <param name="Strategy">Failure supervision strategy</param>
        /// <returns>Process ID of the delegator process</returns>
        public static ProcessId randomMapMany <S, T, U>(
            ProcessName Name,
            int Count,
            Func <S> Setup,
            Func <S, U, S> Inbox,
            Func <T, IEnumerable <U> > MapMany,
            ProcessFlags Flags = ProcessFlags.Default,
            State <StrategyContext, Unit> Strategy = null,
            int MaxMailboxSize = ProcessSetting.DefaultMailboxSize,
            string WorkerName  = "worker"
            )
        {
            if (Inbox == null)
            {
                throw new ArgumentNullException(nameof(Inbox));
            }
            if (WorkerName == null)
            {
                throw new ArgumentNullException(nameof(WorkerName));
            }
            if (Count < 1)
            {
                throw new ArgumentException($"{nameof(Count)} should be greater than 0");
            }

            return(spawn <Unit, T>(
                       Name,
                       () =>
            {
                spawnMany(Count, WorkerName, Setup, Inbox, Flags);
                return unit;
            },
                       (_, msg) =>
            {
                var us = MapMany(msg);

                foreach (var u in us)
                {
                    var index = Prelude.random(Children.Count);
                    var child = Children.Skip(index).Take(1).ToArray();

                    if (child.Length == 0)
                    {
                        throw new NoRouterWorkersException();
                    }
                    else
                    {
                        fwd(child[0].Value, u);
                    }
                }
                return unit;
            },
                       Flags,
                       Strategy,
                       MaxMailboxSize
                       ));
        }
示例#19
0
 /// <summary>
 /// Create a new process by name (accepts Unit as a return value instead of void).
 /// If this is called from within a process' message loop
 /// then the new process will be a child of the current process.  If it is called from
 /// outside of a process, then it will be made a child of the root 'user' process.
 /// </summary>
 /// <typeparam name="T">Type of messages that the child-process can accept</typeparam>
 /// <param name="Name">Name of the child-process</param>
 /// <param name="Inbox">Function that is the process</param>
 /// <param name="Flags">Process flags</param>
 /// <param name="Strategy">Failure supervision strategy</param>
 /// <param name="Terminated">Message function to call when a Process [that this Process
 /// watches] terminates</param>
 /// <returns>A ProcessId that identifies the child</returns>
 public static ProcessId spawnUnit <T>(
     ProcessName Name,
     Func <T, Unit> Inbox,
     ProcessFlags Flags = ProcessFlags.Default,
     State <StrategyContext, Unit> Strategy = null,
     int MaxMailboxSize = ProcessSetting.DefaultMailboxSize,
     Func <ProcessId, Unit> Terminated = null
     ) =>
 spawn <Unit, T>(Name, () => unit, (state, msg) => { Inbox(msg); return(state); }, Flags, Strategy, MaxMailboxSize, (state, pid) => { Terminated(pid); return(state); });
示例#20
0
        public static Either <Exception, ProcessId> TryParse(string path)
        {
            if (path == null || path.Length == 0)
            {
                return(new InvalidProcessIdException());
            }
            if (path[0] == '@')
            {
                return(ParseRegisteredProcess(path.Substring(1)));
            }

            if (path[0] != Sep)
            {
                return(new InvalidProcessIdException());
            }

            ProcessName[] parts = null;
            ProcessName   name;
            string        finalPath = null;

            if (path.Length == 1)
            {
                parts = new ProcessName[0];
            }
            else
            {
                try
                {
                    parts = SplitOnSep(path).ToArray();
                }
                catch (InvalidProcessNameException)
                {
                    return(new InvalidProcessIdException());
                }
            }

            finalPath = parts == null
                ? ""
                : parts.Length == 0
                    ? Sep.ToString()
                    : Sep + String.Join(Sep.ToString(), parts);

            if (path != Sep.ToString())
            {
                name = parts == null
                    ? ""
                    : parts.Length == 0
                        ? Sep.ToString()
                        : parts.Last();
            }
            else
            {
                name = "$";
            }

            return(new ProcessId(parts, name, finalPath));
        }
示例#21
0
 public static ProcessId Register(ProcessName name, ProcessId process) =>
 map(registered as IProcessInternal,
     self => match(self.GetChildProcess(name),
                   Some: _ => failwith <IProcess>("Process '" + name + "' already registered"),
                   None: () => self.AddChildProcess(new ActorProxy(
                                                        registered.Id,
                                                        name,
                                                        ActorProxyTemplate.Registered,
                                                        () => new ActorProxyConfig(process))))).Id;
示例#22
0
 /// <summary>
 /// Spawn by type
 /// </summary>
 /// <typeparam name="TProcess">Process type</typeparam>
 /// <typeparam name="TMsg">Message type</typeparam>
 /// <param name="name">Name of process to spawn</param>
 /// <returns>ProcessId</returns>
 public static ProcessId spawn <TProcess, TMsg>(ProcessName name)
     where TProcess : IProcess <TMsg>, new()
 {
     return(spawn <IProcess <TMsg>, TMsg>(name, () => new TProcess(),
                                          (process, msg) => {
         process.OnMessage(msg);
         return process;
     }));
 }
 public static ProcessId spawnRoundRobin <T>(
     ProcessName Name,
     int Count,
     Action <T> Inbox,
     ProcessFlags Flags = ProcessFlags.Default,
     State <StrategyContext, Unit> Strategy = null,
     int MaxMailboxSize = ProcessSetting.DefaultMailboxSize
     ) =>
 Router.roundRobin(Name, Count, Inbox, Flags, Strategy, MaxMailboxSize);
 /// <summary>
 /// Spawns a new process with Count worker processes, each message is
 /// sent to the least busy worker.
 /// </summary>
 /// <typeparam name="S">State type</typeparam>
 /// <typeparam name="T">Message type</typeparam>
 /// <param name="Name">Delegator process name</param>
 /// <param name="Count">Number of worker processes</param>
 /// <param name="Inbox">Worker message handler</param>
 /// <param name="Flags">Process flags</param>
 /// <param name="Strategy">Failure supervision strategy</param>
 /// <returns>Process ID of the delegator process</returns>
 public static ProcessId leastBusy <T>(
     ProcessName Name,
     int Count,
     Action <T> Inbox,
     ProcessFlags Flags = ProcessFlags.Default,
     State <StrategyContext, Unit> Strategy = null,
     int MaxMailboxSize = ProcessSetting.DefaultMailboxSize,
     string WorkerName  = "worker"
     ) =>
 leastBusy <Unit, T>(Name, Count, () => unit, (_, msg) => { Inbox(msg); return(unit); }, Flags, Strategy, MaxMailboxSize, WorkerName);
示例#25
0
 /// <param name="nodeName">Unique name for this process.  It becomes the name of the root
 /// node and allows other services on the cluster to discover you and communicate with you.
 /// </param>
 /// <param name="connectionString">Provider defined connection string</param>
 /// <param name="catalogueName">>Provider defined catalogue name</param>
 public static ClusterConfig config(
     ProcessName nodeName,
     string connectionString,
     string catalogueName
     ) =>
 new ClusterConfig(
     nodeName,
     connectionString,
     catalogueName
     );
示例#26
0
 public ClusterConfig(
     ProcessName nodeName,
     string connectionString,
     string catalogueName
 )
 {
     NodeName = nodeName;
     ConnectionString = connectionString;
     CatalogueName = catalogueName;
 }
示例#27
0
 /// <param name="nodeName">Unique name for this process.  It becomes the name of the root 
 /// node and allows other services on the cluster to discover you and communicate with you.
 /// </param>
 /// <param name="connectionString">Provider defined connection string</param>
 /// <param name="catalogueName">>Provider defined catalogue name</param>
 public static ClusterConfig config(
     ProcessName nodeName,
     string connectionString,
     string catalogueName
 ) => 
     new ClusterConfig(
         nodeName, 
         connectionString, 
         catalogueName
     );
示例#28
0
 public ClusterConfig(
     ProcessName nodeName,
     string connectionString,
     string catalogueName
     )
 {
     NodeName         = nodeName;
     ConnectionString = connectionString;
     CatalogueName    = catalogueName;
 }
示例#29
0
 /// <summary>
 /// Create a new process by name.
 /// If this is called from within a process' message loop
 /// then the new process will be a child of the current process.  If it is called from
 /// outside of a process, then it will be made a child of the root 'user' process.
 /// </summary>
 /// <typeparam name="T">Type of messages that the child-process can accept</typeparam>
 /// <param name="Name">Name of the child-process</param>
 /// <param name="Setup">Startup and restart function</param>
 /// <param name="Inbox">Function that is the process</param>
 /// <param name="Flags">Process flags</param>
 /// <param name="Strategy">Failure supervision strategy</param>
 /// <param name="Terminated">Message function to call when a Process [that this Process
 /// watches] terminates</param>
 /// <returns>A ProcessId that identifies the child</returns>
 public static ProcessId spawn <S, T>(
     ProcessName Name,
     Func <S> Setup,
     Func <S, T, S> Inbox,
     ProcessFlags Flags = ProcessFlags.Default,
     State <StrategyContext, Unit> Strategy = null,
     int MaxMailboxSize = ProcessSetting.DefaultMailboxSize,
     Func <S, ProcessId, S> Terminated = null
     ) =>
 ActorContext.ActorCreate(ActorContext.SelfProcess, Name, Inbox, Setup, Terminated, Strategy, Flags, MaxMailboxSize, false);
示例#30
0
 /// <summary>
 /// Create N child processes.
 /// The name provided will be used as a basis to generate the child names.  Each child will
 /// be named "name-index" where index starts at zero.
 /// If this is called from within a process' message loop
 /// then the new processes will be a children of the current process.  If it is called from
 /// outside of a process, then they will be made a child of the root 'user' process.
 /// </summary>
 /// <typeparam name="T">Type of messages that the child-process can accept</typeparam>
 /// <param name="Count">Number of processes to spawn</param>
 /// <param name="Name">Name of the child-process</param>
 /// <param name="Inbox">Function that is the process</param>
 /// <param name="Flags">Process flags</param>
 /// <param name="Strategy">Failure supervision strategy</param>
 /// <param name="Terminated">Message function to call when a Process [that this Process
 /// watches] terminates</param>
 /// <returns>ProcessId IEnumerable</returns>
 public static IEnumerable <ProcessId> spawnMany <T>(
     int Count,
     ProcessName Name,
     Action <T> Inbox,
     ProcessFlags Flags = ProcessFlags.Default,
     State <StrategyContext, Unit> Strategy = null,
     int MaxMailboxSize            = ProcessSetting.DefaultMailboxSize,
     Action <ProcessId> Terminated = null
     ) =>
 List.map(Range(0, Count), n => spawn($"{Name}-{n}", Inbox, Flags, Strategy, MaxMailboxSize, Terminated)).ToList();
示例#31
0
 /// <summary>
 /// Create N child processes.
 /// The name provided will be used as a basis to generate the child names.  Each child will
 /// be named "name-index" where index starts at zero.
 /// If this is called from within a process' message loop
 /// then the new processes will be a children of the current process.  If it is called from
 /// outside of a process, then they will be made a child of the root 'user' process.
 /// </summary>
 /// <typeparam name="S">State type</typeparam>
 /// <typeparam name="T">Type of messages that the child-process can accept</typeparam>
 /// <param name="Spec">Map of IDs and State for generating child workers</param>
 /// <param name="Name">Name of the child-process</param>
 /// <param name="Inbox">Function that is the process</param>
 /// <param name="Flags">Process flags</param>
 /// <param name="Strategy">Failure supervision strategy</param>
 /// <param name="Terminated">Message function to call when a Process [that this Process
 /// watches] terminates</param>
 /// <returns>ProcessId IEnumerable</returns>
 public static IEnumerable <ProcessId> spawnMany <S, T>(
     ProcessName Name,
     Map <int, Func <S> > Spec,
     Func <S, T, S> Inbox,
     ProcessFlags Flags = ProcessFlags.Default,
     State <StrategyContext, Unit> Strategy = null,
     int MaxMailboxSize = ProcessSetting.DefaultMailboxSize,
     Func <S, ProcessId, S> Terminated = null
     ) =>
 Map.map(Spec, (id, state) => ActorContext.ActorCreate(ActorContext.SelfProcess, $"{Name}-{id}", Inbox, state, Terminated, Strategy, Flags, MaxMailboxSize, false)).Values.ToList();
示例#32
0
 public static ProcessId ActorCreate <S, T>(
     ActorItem parent,
     ProcessName name,
     Action <T> actorFn,
     Func <S, ProcessId, S> termFn,
     State <StrategyContext, Unit> strategy,
     ProcessFlags flags,
     int maxMailboxSize,
     bool lazy
     ) =>
 ActorCreate <S, T>(parent, name, (s, t) => { actorFn(t); return(default(S)); }, () => default(S), termFn, strategy, flags, maxMailboxSize, lazy);
示例#33
0
 /// <summary>
 /// Connect to a cluster
 /// </summary>
 /// <param name="providerName">Provider name is a unique name for the persistence layer 
 /// type, for example: "redis"</param>
 /// <param name="nodeName">Unique name for this process.  It becomes the name of the root 
 /// node and allows other services on the cluster to discover you and communicate with you.
 /// </param>
 /// <param name="connectionString">Provider defined connection string</param>
 /// <param name="catalogueName">>Provider defined catalogue name</param>
 public static Unit connect(
     string providerName,
     ProcessName nodeName,
     string connectionString,
     string catalogueName
 )
 {
     var cluster = ClusterFactory.CreateCluster(providerName, config(nodeName,connectionString,catalogueName));
     cluster.Connect();
     return ActorContext.RegisterCluster(cluster);
 }
示例#34
0
 /// <summary>
 /// Create N child processes.
 /// The name provided will be used as a basis to generate the child names.  Each child will
 /// be named "name-index" where index starts at zero.
 /// If this is called from within a process' message loop
 /// then the new processes will be a children of the current process.  If it is called from
 /// outside of a process, then they will be made a child of the root 'user' process.
 /// </summary>
 /// <typeparam name="T">Type of messages that the child-process can accept</typeparam>
 /// <param name="Count">Number of processes to spawn</param>
 /// <param name="Setup">Startup and restart function</param>
 /// <param name="Name">Name of the child-process</param>
 /// <param name="Inbox">Function that is the process</param>
 /// <param name="Flags">Process flags</param>
 /// <param name="Strategy">Failure supervision strategy</param>
 /// <param name="Terminated">Message function to call when a Process [that this Process
 /// watches] terminates</param>
 /// <returns>ProcessId IEnumerable</returns>
 public static IEnumerable <ProcessId> spawnMany <S, T>(
     int Count,
     ProcessName Name,
     Func <S> Setup,
     Func <S, T, S> Inbox,
     ProcessFlags Flags = ProcessFlags.Default,
     State <StrategyContext, Unit> Strategy = null,
     int MaxMailboxSize = ProcessSetting.DefaultMailboxSize,
     Func <S, ProcessId, S> Terminated = null
     ) =>
 List.map(Range(0, Count), n => ActorContext.ActorCreate(ActorContext.SelfProcess, $"{Name}-{n}", Inbox, Setup, Terminated, Strategy, Flags, MaxMailboxSize, false)).ToList();
示例#35
0
 /// <summary>
 /// Spawns a new process with N worker processes, each message is mapped
 /// from T to IEnumerable U before each resulting U is passed to the worker
 /// processes in a round robin fashion.
 /// </summary>
 /// <typeparam name="T">Message type</typeparam>
 /// <typeparam name="U">Mapped message type</typeparam>
 /// <param name="Name">Delegator process name</param>
 /// <param name="Count">Number of worker processes</param>
 /// <param name="map">Maps the message from T to IEnumerable U before each one is passed to the workers</param>
 /// <param name="Inbox">Worker message handler</param>
 /// <param name="Flags">Process flags</param>
 /// <param name="Strategy">Failure supervision strategy</param>
 /// <returns>Process ID of the delegator process</returns>
 public static ProcessId randomMapMany <T, U>(
     ProcessName Name,
     int Count,
     Action <U> Inbox,
     Func <T, IEnumerable <U> > MapMany,
     ProcessFlags Flags = ProcessFlags.Default,
     State <StrategyContext, Unit> Strategy = null,
     int MaxMailboxSize = ProcessSetting.DefaultMailboxSize,
     string WorkerName  = "worker"
     ) =>
 randomMapMany(Name, Count, () => unit, (_, umsg) => { Inbox(umsg); return(unit); }, MapMany, Flags, Strategy, MaxMailboxSize, WorkerName);
示例#36
0
 /// <param name="nodeName">Unique name for this process.  It becomes the name of the root 
 /// node and allows other services on the cluster to discover you and communicate with you.
 /// </param>
 /// <param name="connectionString">Provider defined connection string</param>
 /// <param name="catalogueName">>Provider defined catalogue name</param>
 /// <param name="metadata">Provider speific metadata</param>
 public static ClusterConfig config(
     ProcessName nodeName,
     string connectionString,
     string catalogueName,
     Map<string, string> metadata = null
 ) => 
     new ClusterConfig(
         nodeName, 
         connectionString, 
         catalogueName, 
         metadata
     );
示例#37
0
 /// <summary>
 /// Connect to a cluster
 /// </summary>
 /// <param name="providerName">Provider name is a unique name for the persistence layer 
 /// type, for example: "redis"</param>
 /// <param name="nodeName">Unique name for this process.  It becomes the name of the root 
 /// node and allows other services on the cluster to discover you and communicate with you.
 /// </param>
 /// <param name="connectionString">Provider defined connection string</param>
 /// <param name="catalogueName">>Provider defined catalogue name</param>
 public static ICluster connect(
     string providerName,
     ProcessName nodeName,
     string connectionString,
     string catalogueName,
     ProcessName role
 )
 {
     var cluster = ClusterFactory.CreateCluster(providerName, config(nodeName,connectionString,catalogueName, role));
     cluster.Connect();
     return cluster;
 }
示例#38
0
 public ClusterConfig(
     ProcessName nodeName,
     string connectionString,
     string catalogueName,
     Map<string, string> metadata = null
 )
 {
     NodeName = nodeName;
     ConnectionString = connectionString;
     CatalogueName = catalogueName;
     Metadata = metadata ?? Map.empty<string,string>();
 }
示例#39
0
        public ProcessId(string path)
        {
            if (path == null || path.Length == 0)
            {
                throw new InvalidProcessIdException();
            }
            if (path[0] != Sep)
            {
                throw new InvalidProcessIdException();
            }
            if (path.Length == 1)
            {
                parts = new ProcessName[0];
            }
            else
            {
                try
                {
                    parts = path.Substring(1)
                                .Split(Sep)
                                .Select(p => new ProcessName(p))
                                .ToArray();
                }
                catch (InvalidProcessNameException)
                {
                    throw new InvalidProcessIdException();
                }
            }

            Path = parts == null
                ? ""
                : parts.Length == 0
                    ? Sep.ToString()
                    : Sep + String.Join(Sep.ToString(), parts);

            if (path != "/")
            {
                name = parts == null
                    ? ""
                    : parts.Length == 0
                        ? Sep.ToString()
                        : parts.Last();
            }
            else
            {
                name = "$";
            }
        }
示例#40
0
        /// <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;
        }
示例#41
0
        public SessionManager(Option<ICluster> cluster, SystemName system, ProcessName nodeName, VectorConflictStrategy strategy)
        {
            this.cluster = cluster;
            this.system = system;
            this.nodeName = nodeName;

            Sync = new SessionSync(system, nodeName, strategy);

            cluster.Iter(c =>
            {
                notify = c.SubscribeToChannel<SessionAction>(SessionsNotify).Subscribe(act => Sync.Incoming(act));

                var now = DateTime.UtcNow;

                // Look for stranded sessions that haven't been removed properly.  This is done once only
                // on startup because the systems should be shutting down sessions on their own.  This just
                // catches the situation where an app-domain died without shutting down properly.
                c.QuerySessionKeys()
                 .Map(key =>
                    from ts in c.GetHashField<long>(key, LastAccessKey)
                    from to in c.GetHashField<int>(key, TimeoutKey)
                    where new DateTime(ts) < now.AddSeconds(to * 2) // Multiply by 2, just to catch genuine non-active sessions
                    select c.Delete(key))
                 .Iter(id => { });

                // Remove session keys when an in-memory session ends
                ended = SessionEnded.Subscribe(sid => Stop(sid));

                touch = Sync.Touched.Subscribe(tup =>
                {
                    try
                    {
                        c.HashFieldAddOrUpdate(SessionKey(tup.Item1), LastAccessKey, DateTime.UtcNow.Ticks);
                        c.PublishToChannel(SessionsNotify, SessionAction.Touch(tup.Item1, system, nodeName));
                    }
                    catch(Exception e)
                    {
                        logErr(e);
                    }
                });
            });
        }
示例#42
0
 public ClusterNode(ProcessName nodeName, DateTime lastHeartbeat, ProcessName role)
 {
     NodeName = nodeName;
     LastHeartbeat = lastHeartbeat;
     Role = role;
 }
示例#43
0
 /// <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) =>
     ActorContext.DeregisterByName($"{Role.Current.Value}-{name.Value}");
示例#44
0
 internal NodeOnline(ProcessName name)
 {
     Name = name;
 }
示例#45
0
 /// <summary>
 /// Removes the dispatcher registration for the named dispatcher
 /// </summary>
 /// <param name="name">Name of the dispatcher to remove</param>
 public static Unit deregister(ProcessName name) =>
     ActorContext.RemoveDispatcher(name);
 public static ProcessId Register(ProcessName name, ProcessId process) =>
     map(registered as IProcessInternal,
         self => match(self.GetChildProcess(name),
             Some: _ => failwith<IProcess>("Process '"+ name + "' already registered"),
             None: () => self.AddChildProcess( new ActorProxy(
                                                 registered.Id,
                                                 name,
                                                 ActorProxyTemplate.Registered,
                                                 () => new ActorProxyConfig(process) ) ) ) ).Id;
示例#47
0
 public State SetMember(ProcessName nodeName, ClusterNode state) =>
     new State(Members.AddOrUpdate(nodeName, state));
示例#48
0
 public ProcessId MakeChildId(ProcessName name) =>
     parts == null
         ? failwith<ProcessId>("ProcessId is None")
         : parts.Length == 0
             ? new ProcessId("" + Sep + name)
             : new ProcessId(Path + Sep + name);
示例#49
0
 /// <summary>
 /// Get the child processes by name
 /// </summary>
 public static ProcessId child(ProcessName name) =>
     InMessageLoop
         ? Self[name]
         : raiseUseInMsgLoopOnlyException<ProcessId>(nameof(child));
示例#50
0
 public static Option<Func<ProcessId, IEnumerable<ProcessId>>> getFuncOption(ProcessName name) =>
     dispatchers.Find(name);
示例#51
0
 public static Func<ProcessId, IEnumerable<ProcessId>> getFunc(ProcessName name) =>
     dispatchers.Find(name,
         Some: x => x,
         None: () => (ProcessId Id) => (new ProcessId[0]).AsEnumerable()
         );
 public static Unit UnRegister(ProcessName name) =>
     map(registered.Id + ProcessId.Sep.ToString() + name,
         id =>
             Store.ContainsKey(id)
                 ? Process.kill(id)
                 : unit);
示例#53
0
 /// <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="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.Disp["reg"][$"{role.Value}-{name.Value}"];
示例#54
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>());
示例#55
0
 public State RemoveMember(ProcessName nodeName) =>
     new State(Members.Remove(nodeName));
示例#56
0
 /// <summary>
 /// Registers a dispatcher for a role
 /// Dispatchers take in a 'leaf' ProcessId (i.e. /user/my-process) and return an enumerable
 /// of real ProcessIds that the Process system will use to deliver all of the standard functions
 /// like tell, ask, subscribe, etc.
 /// </summary>
 /// <param name="name">Name of the dispatcher</param>
 /// <param name="selector">Function that will be invoked every time a dispatcher based ProcessId
 /// is used.</param>
 /// <returns>A root dispatcher ProcessId.  Use this to create new ProcessIds that will
 /// be passed to the selector function whenever the dispatcher based ProcessId is used</returns>
 public static ProcessId register(ProcessName name, Func<ProcessId, IEnumerable<ProcessId>> selector) =>
     ActorContext.AddDispatcher(name, selector);
示例#57
0
 /// <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.Register($"{Role.Current.Value}-{name.Value}", process);
示例#58
0
 public State SetMember(ProcessName nodeName, ClusterNode state) =>
     state == null
         ? RemoveMember(nodeName)
         : new State(Members.AddOrUpdate(nodeName, state), System);
示例#59
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) =>
     ActorContext.Cluster
                 .Map(c => c.QueryRegistered(role.Value, keyQuery))
                 .IfNone(List.empty<ProcessName>());
示例#60
0
 internal static Unit init(ProcessName name)
 {
     Current = name;
     return unit;
 }