Пример #1
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,
            ProcessSystemConfig settings,
            IActorSystem sys
            )
        {
            setupFn = setup ?? throw new ArgumentNullException(nameof(setup));
            actorFn = actor ?? throw new ArgumentNullException(nameof(actor));

            this.sys     = sys;
            Id           = parent.Actor.Id[name];
            this.cluster = cluster;
            this.flags   = flags == ProcessFlags.Default
                ? settings.GetProcessFlags(Id)
                : flags;

            termFn = term;

            Parent   = parent;
            Name     = name;
            Strategy = strategy;
            SetupRemoteSubscriptions(cluster, flags);
        }
Пример #2
0
        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;
        }
Пример #3
0
 public Unit UpdateSettings(ProcessSystemConfig settings, AppProfile profile)
 {
     this.settings   = settings;
     this.appProfile = profile;
     // TODO: Consider notification system for Processes
     return(unit);
 }
Пример #4
0
 /// <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);
     }
 }
Пример #5
0
        /// <summary>
        /// Process system configuration initialisation
        /// This will parse the configuration text, you should call this
        /// function from within Application_BeginRequest of Global.asax.  It can run multiple times, once the config
        /// has loaded the system won't re-load the config until you call ProcessConfig.unload() followed by
        /// ProcessConfig.initialiseFileSystem(...), so it's safe to not surround it with ifs.
        ///
        /// NOTE: If a cluster is specified in the cluster.conf and its 'node-name' matches nodeName, then those settings
        /// will be used to connect to the cluster.  This allows for different staging environments to be setup.
        /// </summary>
        /// <param name="nodeName">If a cluster is specified in the cluster.conf and its 'node-name' matches nodeName, then
        /// those settings will be used to connect to the cluster.  This allows for different staging environments to be
        /// setup.</param>
        /// <param name="setup">A setup function to call on successful loading of the configuration files - this will
        /// happen once only.</param>
        /// <param name="strategyFuncs">Plugin extra strategy behaviours by passing in a list of FuncSpecs.</param>
        public static AppProfile initialise(string configText, Option <string> nodeName, Action <AppProfile> setup = null, IEnumerable <FuncSpec> strategyFuncs = null)
        {
            if (appProfile != null)
            {
                return(appProfile);
            }
            lock (sync)
            {
                if (appProfile != null)
                {
                    return(appProfile);
                }

                config = new ProcessSystemConfig(nodeName.IfNone(""), strategyFuncs);
                config.ParseConfigText(configText);

                appProfile = AppProfile.NonClustered;

                nodeName.Iter(node =>
                {
                    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.GetClusterSetting <string>("env", "value");
                    var userEnv     = config.GetClusterSetting <string>("user-env", "value");

                    appProfile = new AppProfile(
                        node,
                        role,
                        clusterConn,
                        clusterDb,
                        env,
                        userEnv
                        );

                    Cluster.connect(provider, node, clusterConn, clusterDb, role);
                });

                config.PostConnect();

                setup(appProfile);
                return(appProfile);
            }
        }
Пример #6
0
        public static Unit StartSystem(SystemName system, Option<ICluster> cluster, AppProfile appProfile, ProcessSystemConfig config)
        {
            lock (sync)
            {
                if (systems.ContainsKey(system))
                {
                    throw new InvalidOperationException($"Process-system ({system}) already started");
                }

                var asystem = new ActorSystem(system, cluster, appProfile, config);
                systems.AddOrUpdate(system, asystem, (_, __) => asystem);
                try
                {
                    asystem.Initialise();

                    // Set the default system if the 'default: yes' setting is in the ProcessSystemConfig
                    defaultSystem = defaultSystem.IsValid
                        ? (from c in config.Cluster
                           where c.Default
                           select system)
                          .IfNone(defaultSystem)
                        : system;
                }
                catch
                {
                    systems.TryRemove(system, out asystem);
                    try
                    {
                        asystem.Dispose();
                    }
                    catch { }
                    throw;
                }
                return unit;
            }
        }
Пример #7
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);
                });
            }
        }
Пример #8
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;
            Ping             = new Ping(this);
            startupTimestamp = DateTime.UtcNow.Ticks;
            sessionManager   = new SessionManager(cluster, SystemName, appProfile.NodeName, VectorConflictStrategy.Branch);
            watchers         = Map <ProcessId, Set <ProcessId> >();
            watchings        = Map <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);

            Root        = rootItem.Actor.Id;
            RootJS      = Root["js"];
            System      = Root[ActorSystemConfig.Default.SystemProcessName];
            User        = Root[ActorSystemConfig.Default.UserProcessName];
            Errors      = System[ActorSystemConfig.Default.ErrorsProcessName];
            DeadLetters = System[ActorSystemConfig.Default.DeadLettersProcessName];
            NodeName    = cluster.Map(c => c.NodeName).IfNone("user");
            AskId       = System[ActorSystemConfig.Default.AskProcessName];
            Disp        = ProcessId.Top["disp"].SetSystem(SystemName);

            userContext = new ActorRequestContext(
                this,
                rootProcess.Children["user"],
                ProcessId.NoSender,
                rootItem,
                null,
                null,
                ProcessFlags.Default,
                null);
            rootInbox.Startup(rootProcess, parent, cluster, settings.GetProcessMailboxSize(rootProcess.Id));
        }
Пример #9
0
        public static Unit StartSystem(SystemName system, Option <ICluster> cluster, AppProfile appProfile, ProcessSystemConfig config)
        {
            lock (sync)
            {
                if (systems.ContainsKey(system))
                {
                    throw new InvalidOperationException($"Process-system ({system}) already started");
                }

                var asystem = new ActorSystem(system, cluster, appProfile, config);
                systems.AddOrUpdate(system, asystem, (_, __) => asystem);
                try
                {
                    asystem.Initialise();

                    // Set the default system if the 'default: yes' setting is in the ProcessSystemConfig
                    defaultSystem = defaultSystem.IsValid
                        ? (from c in config.Cluster
                           where c.Default
                           select system)
                                    .IfNone(defaultSystem)
                        : system;
                }
                catch
                {
                    systems.TryRemove(system, out asystem);
                    try
                    {
                        asystem.Dispose();
                    }
                    catch { }
                    throw;
                }
                return(unit);
            }
        }
Пример #10
0
        public static Unit StartSystem(SystemName system, Option <ICluster> cluster, AppProfile appProfile, ProcessSystemConfig config)
        {
            lock (sync)
            {
                if (SystemExists(system))
                {
                    throw new InvalidOperationException($"Process-system ({system}) already started");
                }

                var asystem = new ActorSystem(system, cluster, appProfile, config);
                AddOrUpdateSystem(asystem);

                try
                {
                    asystem.Initialise();

                    // Set the default system if the 'default: yes' setting is in the ProcessSystemConfig
                    defaultSystem = defaultSystem.IsValid
                        ? (from c in config.Cluster
                           where c.Default
                           select system)
                                    .IfNone(defaultSystem)
                        : system;
                }
                catch
                {
                    systems = systems.Filter(a => a.SystemName != system).ToArray();
                    try
                    {
                        asystem.Dispose();
                    }
                    catch { }
                    throw;
                }
                return(unit);
            }
        }
Пример #11
0
        public void ProcessesSettingsParserTest()
        {
            var text = @"
            
                time timeout:           30 seconds
                time session-timeout:   60 seconds
                int mailbox-size:      10000

                strategy my-strategy:
                    one-for-one:
                        retries: count = 5, duration=30 seconds
                        backoff: min = 2 seconds, max = 1 hour, step = 5 seconds
                        
                        match
                         | System.NotImplementedException -> stop
                         | System.ArgumentNullException   -> escalate
                         | _                              -> restart

                        redirect when
                         | restart  -> forward-to-parent
                         | escalate -> forward-to-self
                         | stop     -> forward-to-process /root/test/567
                         | _        -> forward-to-dead-letters

                strategy my-other-strategy:
                    one-for-one:
                        pause: 1 second
                        always: resume
                        redirect: forward-to-process /root/test/567

                process abc:
                    pid:          /root/test/123
                    flags:        [persist-inbox, persist-state, remote-publish]
                    mailbox-size: 1000
                    strategy:     my-strategy

                process def:
                    pid:          /root/test/567
                    flags:        [persist-inbox, persist-state]
                    mailbox-size: 100
                    strategy:     my-other-strategy
                ";

            var config = new ProcessSystemConfig("test");

            var res = parse(config.Parser, text);

            // TODO: Restore tests

            //Assert.False(res.IsFaulted);

            //var result = res.Reply.Result;

            //Assert.True(result.Count == 7);


            //var timeout = result["timeout"];
            //var session = result["session-timeout"];
            //var mailbox= result["mailbox-size"];

            // Load process settings
            //var processes = M.createRange(from val in result.Values
            //                              where val.Spec.Args.Length > 0 && val.Spec.Args[0].Type.Tag == ArgumentTypeTag.Process
            //                              let p = (ProcessToken)val.Values.Values.First().Value
            //                              where p.ProcessId.IsSome
            //                              let id = p.ProcessId.IfNone(ProcessId.None)
            //                              select Tuple(id, p));

            //var strats = M.createRange(from val in result.Values
            //                           where val.Spec.Args.Length > 0 && val.Spec.Args[0].Type.Tag == ArgumentTypeTag.Strategy
            //                           let s = (StrategyToken)val.Values.Values.First().Value
            //                           select Tuple(val.Name, s));


            //Assert.True(timeout.Name == "timeout");
            //Assert.True(timeout.Attributes.Count == 1);
            //Assert.True(timeout.Attributes["value"].Type.Tag == ArgumentTypeTag.Time);
            //Assert.True((Time)timeout.Attributes["value"].Value == 30*seconds);

            //Assert.True(session.Name == "session-timeout");
            //Assert.True(session.Attributes.Count == 1);
            //Assert.True(session.Attributes["value"].Type.Tag == ArgumentTypeTag.Time);
            //Assert.True((Time)session.Attributes["value"].Value == 60 * seconds);

            //Assert.True(mailbox.Name == "mailbox-size");
            //Assert.True(mailbox.Attributes.Count == 1);
            //Assert.True(mailbox.Attributes["value"].Type.Tag == ArgumentTypeTag.Int);
            //Assert.True((int)mailbox.Attributes["value"].Value == 10000);

            //Assert.True(strats.Count == 2);
            //Assert.True(processes.Count == 2);
        }
Пример #12
0
        public void ProcessesSettingsParserTest()
        {
            var text = @"
            
                time timeout:           30 seconds
                time session-timeout:   60 seconds
                int mailbox-size:      10000

                strategy my-strategy:
                    one-for-one:
                        retries: count = 5, duration=30 seconds
                        backoff: min = 2 seconds, max = 1 hour, step = 5 seconds
                        
                        match
                         | System.NotImplementedException -> stop
                         | System.ArgumentNullException   -> escalate
                         | _                              -> restart

                        redirect when
                         | restart  -> forward-to-parent
                         | escalate -> forward-to-self
                         | stop     -> forward-to-process /root/test/567
                         | _        -> forward-to-dead-letters

                strategy my-other-strategy:
                    one-for-one:
                        pause: 1 second
                        always: resume
                        redirect: forward-to-process /root/test/567

                process abc:
                    pid:          /root/test/123
                    flags:        [persist-inbox, persist-state, remote-publish]
                    mailbox-size: 1000
                    strategy:     my-strategy

                process def:
                    pid:          /root/test/567
                    flags:        [persist-inbox, persist-state]
                    mailbox-size: 100
                    strategy:     my-other-strategy
                ";

            var config = new ProcessSystemConfig("test");

            var res = parse(config.Parser, text);

            // TODO: Restore tests

            //Assert.False(res.IsFaulted);

            //var result = res.Reply.Result;

            //Assert.True(result.Count == 7);


            //var timeout = result["timeout"];
            //var session = result["session-timeout"];
            //var mailbox= result["mailbox-size"];

            // Load process settings
            //var processes = M.createRange(from val in result.Values
            //                              where val.Spec.Args.Length > 0 && val.Spec.Args[0].Type.Tag == ArgumentTypeTag.Process
            //                              let p = (ProcessToken)val.Values.Values.First().Value
            //                              where p.ProcessId.IsSome
            //                              let id = p.ProcessId.IfNone(ProcessId.None)
            //                              select Tuple(id, p));

            //var strats = M.createRange(from val in result.Values
            //                           where val.Spec.Args.Length > 0 && val.Spec.Args[0].Type.Tag == ArgumentTypeTag.Strategy
            //                           let s = (StrategyToken)val.Values.Values.First().Value
            //                           select Tuple(val.Name, s));


            //Assert.True(timeout.Name == "timeout");
            //Assert.True(timeout.Attributes.Count == 1);
            //Assert.True(timeout.Attributes["value"].Type.Tag == ArgumentTypeTag.Time);
            //Assert.True((Time)timeout.Attributes["value"].Value == 30*seconds);

            //Assert.True(session.Name == "session-timeout");
            //Assert.True(session.Attributes.Count == 1);
            //Assert.True(session.Attributes["value"].Type.Tag == ArgumentTypeTag.Time);
            //Assert.True((Time)session.Attributes["value"].Value == 60 * seconds);

            //Assert.True(mailbox.Name == "mailbox-size");
            //Assert.True(mailbox.Attributes.Count == 1);
            //Assert.True(mailbox.Attributes["value"].Type.Tag == ArgumentTypeTag.Int);
            //Assert.True((int)mailbox.Attributes["value"].Value == 10000);

            //Assert.True(strats.Count == 2);
            //Assert.True(processes.Count == 2);

        }
Пример #13
0
 static ProcessConfig()
 {
     config = new ProcessSystemConfig("");
 }