/// <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);
        }
Example #2
0
 public Map <string, T> GetHashFields <T>(string key) =>
 Retry(() =>
       Db.HashGetAll(key)
       .Fold(
           Map.empty <string, T>(),
           (m, e) => m.Add(e.Name, JsonConvert.DeserializeObject <T>(e.Value)))
       .Filter(notnull));
Example #3
0
 public Map <K, T> GetHashFields <K, T>(string key, Func <string, K> keyBuilder) =>
 Retry(() =>
       Db.HashGetAll(key)
       .Fold(
           Map.empty <K, T>(),
           (m, e) => m.Add(keyBuilder(e.Name), JsonConvert.DeserializeObject <T>(e.Value)))
       .Filter(notnull));
 public Map <string, T> GetHashFields <T>(string key, IEnumerable <string> fields) =>
 Db.HashGet(key, fields.Map(x => (RedisValue)x).ToArray())
 .Zip(fields)
 .Filter(x => !x.Item1.IsNullOrEmpty)
 .Fold(
     Map.empty <string, T>(),
     (m, e) => m.Add(e.Item2, JsonConvert.DeserializeObject <T>(e.Item1)));
Example #5
0
        public Unit ShutdownProcess(bool maintainState)
        {
            Parent.Actor.UnlinkChild(Id);
            ShutdownProcessRec(ActorContext.SelfProcess, ActorContext.GetInboxShutdownItem().Map(x => (ILocalActorInbox)x.Inbox), maintainState);

            children = Map.empty <string, ActorItem>();
            return(unit);
        }
Example #6
0
        public static Unit Startup(Option <ICluster> cluster)
        {
            if (started)
            {
                return(unit);
            }

            ActorContext.cluster = cluster;
            var name = GetRootProcessName();

            if (name.Value == "root" && cluster.IsSome)
            {
                throw new ArgumentException("Cluster node name cannot be 'root', it's reserved for local use only.");
            }
            if (name.Value == "disp" && cluster.IsSome)
            {
                throw new ArgumentException("Cluster node name cannot be 'disp', it's reserved for internal use.");
            }
            if (name.Value == "js")
            {
                throw new ArgumentException("Node name cannot be 'js', it's reserved for ProcessJS.");
            }

            lock (sync)
            {
                if (started)
                {
                    return(unit);
                }

                startupTimestamp = DateTime.UtcNow.Ticks;
                startupSubscription?.Dispose();
                startupSubscription = NotifyCluster(cluster, startupTimestamp);

                Dispatch.init();
                Role.init();
                Reg.init();

                watchers  = Map.empty <ProcessId, Set <ProcessId> >();
                watchings = Map.empty <ProcessId, Set <ProcessId> >();
                var root      = ProcessId.Top.Child(name);
                var rootInbox = new ActorInboxLocal <ActorSystemState, Unit>();
                var parent    = new ActorItem(new NullProcess(), new NullInbox(), ProcessFlags.Default);

                var go          = new AutoResetEvent(false);
                var state       = new ActorSystemState(cluster, root, null, rootInbox, cluster.Map(x => x.NodeName).IfNone(ActorConfig.Default.RootProcessName), ActorConfig.Default);
                var rootProcess = state.RootProcess;
                state.Startup();
                userContext = new ActorRequestContext(rootProcess.Children["user"], ProcessId.NoSender, rootItem, null, null, ProcessFlags.Default);
                rootInbox.Startup(rootProcess, parent, cluster, ProcessSetting.DefaultMailboxSize);
                rootItem = new ActorItem(rootProcess, rootInbox, ProcessFlags.Default);
                started  = true;

                SessionManager.Init(cluster);
                ClusterWatch(cluster);
            }
            return(unit);
        }
Example #7
0
        public Unit ShutdownProcess()
        {
            //tellSystem(Parent.Actor.Id, SystemMessage.UnlinkChild(Id));
            Parent.Actor.UnlinkChild(Id);
            ShutdownProcessRec(ActorContext.SelfProcess, ActorContext.GetInboxShutdownItem().Map(x => (ILocalActorInbox)x.Inbox));

            children = Map.empty <string, ActorItem>();
            return(unit);
        }
 /// <summary>
 /// Resets the configuration system to all default settings (i.e. empty).  Use this call followed by
 /// one of the ProcessConfig.initialise(...) variants to reload new configuration settings live.
 /// </summary>
 public static Unit unload()
 {
     lock (sync)
     {
         appProfile      = null;
         config          = new ProcessSystemConfig("");
         processSettings = Map.empty <string, object>();
         return(unit);
     }
 }
 public StrategyState(
     Time backoffAmount,
     int failures,
     DateTime lastFailure,
     Map <string, object> metadata
     )
 {
     BackoffAmount = backoffAmount;
     Failures      = failures;
     LastFailure   = lastFailure;
     Metadata      = metadata ?? Map.empty <string, object>();
 }
Example #10
0
 public Unit ShutdownProcess(bool maintainState)
 {
     lock (sync)
     {
         return(Parent.Actor.Children.Find(Name.Value).IfSome(self =>
         {
             ShutdownProcessRec(self, sys.GetInboxShutdownItem().Map(x => (ILocalActorInbox)x.Inbox), maintainState);
             Parent.Actor.UnlinkChild(Id);
             children = Map.empty <string, ActorItem>();
         }));
     }
 }
Example #11
0
 public static State Empty(IActorSystem system) => new State(Map.empty <ProcessName, ClusterNode>(), system);
Example #12
0
 public State()
 {
     Sessions = Map.empty <string, Session>();
     Metadata = Map.empty <string, object>();
 }
Example #13
0
        /// <summary>
        /// Static ctor
        /// Sets up the default roles
        /// </summary>
        static Role()
        {
            ProcessName first      = "role-first";
            ProcessName second     = "role-second";
            ProcessName third      = "role-third";
            ProcessName last       = "role-last";
            ProcessName next       = "role-next";
            ProcessName prev       = "role-prev";
            ProcessName broadcast  = "role-broadcast";
            ProcessName leastBusy  = "role-least-busy";
            ProcessName random     = "role-random";
            ProcessName roundRobin = "role-round-robin";

            var nextNode = fun((bool fwd) => fun((ProcessId leaf) =>
            {
                var self    = leaf.Take(1).GetName();
                var isNext  = false;
                var nodeMap = Nodes(leaf);

                var nodes = fwd
                    ? nodeMap.Values.Append(nodeMap.Values)
                    : nodeMap.Values.Append(nodeMap.Values).Reverse(); //< TODO: Inefficient

                foreach (var node in nodes)
                {
                    if (isNext)
                    {
                        return(new[] { ProcessId.Top[node.NodeName].Append(leaf.Skip(1)) }.AsEnumerable());
                    }

                    if (node.NodeName == self)
                    {
                        isNext = true;
                    }
                }
                return(new ProcessId[0].AsEnumerable());
            }));

            // Next
            nextRoot = Dispatch.register(next, nextNode(true));

            // Prev
            prevRoot = Dispatch.register(prev, nextNode(false));

            // First
            First = Dispatch.register(first, leaf => NodeIds(leaf).Take(1));

            // Second
            Second = Dispatch.register(second, leaf => NodeIds(leaf).Skip(1).Take(1));

            // Third
            Third = Dispatch.register(third, leaf => NodeIds(leaf).Skip(2).Take(1));

            // Last
            Last = Dispatch.register(last, leaf => NodeIds(leaf).Reverse().Take(1));

            // Broadcast
            Broadcast = Dispatch.register(broadcast, NodeIds);

            // Least busy
            LeastBusy = Dispatch.register(leastBusy, leaf =>
                                          NodeIds(leaf)
                                          .Map(pid => Tuple(inboxCount(pid), pid))
                                          .OrderBy(tup => tup.Item1)
                                          .Map(tup => tup.Item2)
                                          .Take(1));

            // Random
            Random = Dispatch.register(random, leaf => {
                var workers = NodeIds(leaf).ToArray();
                return(new ProcessId[1] {
                    workers[Prelude.random(workers.Length)]
                });
            });

            // Round-robin
            object            sync            = new object();
            Map <string, int> roundRobinState = Map.empty <string, int>();

            RoundRobin = Dispatch.register(roundRobin, leaf => {
                var key     = leaf.ToString();
                var workers = NodeIds(leaf).ToArray();
                int index   = 0;
                lock (sync)
                {
                    roundRobinState = roundRobinState.AddOrUpdate(key, x => { index = x % workers.Length; return(x + 1); }, 0);
                }
                return(new ProcessId[1] {
                    workers[index]
                });
            });
        }
 /// <summary>
 /// Access a map 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 Map <string, T> readMap <T>(string name, string prop = "value") =>
 read(name, prop, Map.empty <string, T>());
Example #15
0
        static Dispatch()
        {
            ProcessName broadcast  = "broadcast";
            ProcessName leastBusy  = "least-busy";
            ProcessName random     = "random";
            ProcessName roundRobin = "round-robin";

            ProcessName first  = "first";
            ProcessName second = "second";
            ProcessName third  = "third";
            ProcessName last   = "last";

            var processes = fun((ProcessId leaf) =>
            {
                if (!leaf.IsValid)
                {
                    return(new ProcessId[0]);
                }
                if (leaf.IsSelection)
                {
                    return(leaf.GetSelection());
                }
                if (leaf.Head().Name == "disp")
                {
                    leaf = leaf.Skip(1);
                    if (!leaf.IsValid)
                    {
                        return(new ProcessId[0]);
                    }
                    return(getFunc(leaf.Head().Name)(leaf.Skip(1)));
                }
                return(new ProcessId[1] {
                    leaf
                });
            });

            // Broadcast
            Broadcast = register(broadcast, processes);

            // First
            First = register(first, leaf => processes(leaf).Take(1));

            // Second
            Second = register(second, leaf => processes(leaf).Skip(1).Take(1));

            // Third
            Third = register(third, leaf => processes(leaf).Skip(2).Take(1));

            // Last
            Last = register(last, leaf => processes(leaf).Reverse().Take(1));

            // Least busy
            LeastBusy = register(leastBusy, leaf =>
                                 processes(leaf)
                                 .Map(pid => Tuple(inboxCount(pid), pid))
                                 .OrderBy(tup => tup.Item1)
                                 .Map(tup => tup.Item2)
                                 .Take(1));

            // Random
            Random = register(random, leaf => {
                var workers = processes(leaf).ToArray();
                return(new ProcessId[1] {
                    workers[Prelude.random(workers.Length)]
                });
            });

            // Round-robin
            object            sync            = new object();
            Map <string, int> roundRobinState = Map.empty <string, int>();

            RoundRobin = register(roundRobin, leaf => {
                var key     = leaf.ToString();
                var workers = processes(leaf).ToArray();
                int index   = 0;
                lock (sync)
                {
                    roundRobinState = roundRobinState.AddOrUpdate(key, x => { index = x % workers.Length; return(x + 1); }, 0);
                }
                return(new ProcessId[1] {
                    workers[index]
                });
            });
        }
 public Map <string, ProcessId> GetChildren() =>
 List.fold(
     MapRoleMembers(disp => disp.GetChildren()),
     Map.empty <string, ProcessId>(),
     (s, x) => s + x
     );
Example #17
0
 private Unit RemoveAllSubscriptions()
 {
     subs.Iter(x => x.Dispose());
     subs = Map.empty <string, IDisposable>();
     return(unit);
 }
Example #18
0
 /// <summary>
 /// Get a list of cluster nodes that are online
 /// </summary>
 public static Map <ProcessName, ClusterNode> ClusterNodes(SystemName system = default(SystemName)) =>
 ActorContext.System(system).ClusterState == null
         ? Map.empty <ProcessName, ClusterNode>()
         : ActorContext.System(system).ClusterState.Members;
Example #19
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 Map <ProcessId, ProcessMetaData> queryProcessMetaData(string keyQuery) =>
 ActorContext.Cluster
 .Map(c => c.QueryProcessMetaData(keyQuery))
 .IfNone(Map.empty <ProcessId, ProcessMetaData>());
 public Map <string, ProcessId> GetChildren() =>
 Map.empty <string, ProcessId>();
Example #21
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 Map <ProcessId, ProcessMetaData> queryProcessMetaData(string keyQuery, SystemName system = default(SystemName)) =>
 ActorContext.System(system).Cluster
 .Map(c => c.QueryProcessMetaData(keyQuery))
 .IfNone(Map.empty <ProcessId, ProcessMetaData>());
Example #22
0
        public ActorSystem(SystemName systemName, Option <ICluster> cluster, AppProfile appProfile, ProcessSystemConfig settings)
        {
            var name = GetRootProcessName(cluster);

            if (name.Value == "root" && cluster.IsSome)
            {
                throw new ArgumentException("Cluster node name cannot be 'root', it's reserved for local use only.");
            }
            if (name.Value == "disp" && cluster.IsSome)
            {
                throw new ArgumentException("Cluster node name cannot be 'disp', it's reserved for internal use.");
            }
            if (name.Value == "js")
            {
                throw new ArgumentException("Node name cannot be 'js', it's reserved for ProcessJS.");
            }

            SystemName       = systemName;
            this.appProfile  = appProfile;
            this.settings    = settings;
            this.cluster     = cluster;
            startupTimestamp = DateTime.UtcNow.Ticks;
            sessionManager   = new SessionManager(cluster, SystemName, appProfile.NodeName, VectorConflictStrategy.Branch);
            watchers         = Map.empty <ProcessId, Set <ProcessId> >();
            watchings        = Map.empty <ProcessId, Set <ProcessId> >();

            startupSubscription = NotifyCluster(cluster, startupTimestamp);

            Dispatch.init();
            Role.init(cluster.Map(r => r.Role).IfNone("local"));
            Reg.init();

            var root      = ProcessId.Top.Child(GetRootProcessName(cluster));
            var rootInbox = new ActorInboxLocal <ActorSystemBootstrap, Unit>();
            var parent    = new ActorItem(new NullProcess(SystemName), new NullInbox(), ProcessFlags.Default);

            var state = new ActorSystemBootstrap(
                this,
                cluster,
                root, null,
                rootInbox,
                cluster.Map(x => x.NodeName).IfNone(ActorSystemConfig.Default.RootProcessName),
                ActorSystemConfig.Default,
                Settings,
                sessionManager.Sync
                );

            var rootProcess = state.RootProcess;

            state.Startup();
            rootItem    = new ActorItem(rootProcess, rootInbox, ProcessFlags.Default);
            userContext = new ActorRequestContext(
                this,
                rootProcess.Children["user"],
                ProcessId.NoSender,
                rootItem,
                null,
                null,
                ProcessFlags.Default,
                null,
                null);
            rootInbox.Startup(rootProcess, parent, cluster, settings.GetProcessMailboxSize(rootProcess.Id));
        }