Esempio n. 1
0
        public Int32 ScriptMain([In] object[] ScriptParameters, [In] Int32 DefaultReturnCode)
        {
            int CurrentLatency = (int)ScriptParameters[0];

            if (FirstRun)
            {
                FirstRun = false;
                Database.ACR_SetPersistentInt(GetModule(), "ACR_HEALTHMONITOR_STATUS", (int)HealthStatus);
                Database.ACR_SetPersistentInt(GetModule(), "ACR_HEALTHMONITOR_VAULT_STATUS", (int)VaultStatus);

                LatencyMeasurements.AddRange(new int[LATENCY_MEASUREMENTS_TO_RECORD]);
            }

            //
            // Record the current latency measurement value.  If we have a full
            // set of history, the process this batch of historical data.
            //

            if (MeasurementIndex == LATENCY_MEASUREMENTS_TO_RECORD)
            {
                MeasurementIndex = 0;
                ProcessMeasuredLatency();
            }

            // WriteTimestampedLogEntry(String.Format("LatencyMeasurements[{0}] = {1}", MeasurementIndex, CurrentLatency));

            //
            // Consider measurement failures as high latency.  Usually these
            // are due to the measurement timing out entirely (2000ms+) anyway.
            //

            if (CurrentLatency == -1)
            {
                CurrentLatency = LATENCY_HIGH_VALUE;
            }

            LatencyMeasurements[MeasurementIndex++] = CurrentLatency;

            return(DefaultReturnCode);
        }
Esempio n. 2
0
        /// <summary>
        /// This method is called when we have a batch of latency measurements
        /// available.  Its purpose is to determine the current server health
        /// status, schedule a database update, and perform adjustment actions
        /// as necessary.
        /// </summary>
        private void ProcessMeasuredLatency()
        {
            LATENCY_HEALTH_STATUS NewStatus;
            VAULT_HEALTH_STATUS   NewVaultStatus;
            int  MedianLatency;
            int  VaultLatency;
            uint ModuleObject = GetModule();

            //
            // Find the median latency for the past several measurements and
            // set the health status of the server based on that.
            //

            LatencyMeasurements.Sort();

            MedianLatency = LatencyMeasurements[LATENCY_MEASUREMENTS_TO_RECORD / 2];

            if (MedianLatency < LATENCY_LOW_VALUE)
            {
                NewStatus = LATENCY_HEALTH_STATUS.Healthy;
            }
            else if (MedianLatency < LATENCY_HIGH_VALUE)
            {
                NewStatus = LATENCY_HEALTH_STATUS.Warning;
            }
            else
            {
                NewStatus = LATENCY_HEALTH_STATUS.Unhealthy;
            }

            SetGlobalInt("ACR_SERVER_LATENCY_MEDIAN", MedianLatency);

            VaultLatency = GetGlobalInt("ACR_VAULT_LATENCY");

            if (VaultLatency == -1)
            {
                VaultLatency = VAULT_LATENCY_HIGH_VALUE;
            }

            if (VaultLatency < VAULT_LATENCY_HIGH_VALUE)
            {
                NewVaultStatus = VAULT_HEALTH_STATUS.Healthy;
            }
            else
            {
                NewVaultStatus = VAULT_HEALTH_STATUS.Unhealthy;
            }

            //
            // Log a message and update the database if the health status has
            // changed.
            //

            if (HealthStatus != NewStatus)
            {
                WriteTimestampedLogEntry(String.Format("ACR_HealthMonitor.ProcessMeasuredLatency(): Server health status is now {0} (median latency {1})", NewStatus, MedianLatency));
                Database.ACR_SetPersistentInt(ModuleObject, "ACR_HEALTHMONITOR_STATUS", (int)NewStatus);

                HealthStatus = NewStatus;

                //
                // Attempt to record some useful data about what is happening
                // on the server when we transition to the unhealthy state.
                //

                if (HealthStatus == LATENCY_HEALTH_STATUS.Unhealthy)
                {
                    DiagnoseServerHealth();
                }
            }

            if (VaultStatus != NewVaultStatus)
            {
                WriteTimestampedLogEntry(String.Format("ACR_HealthMonitor.ProcessMeasuredLatency(): Vault connection health status is now {0} (latency {1})", NewVaultStatus, VaultLatency));
                Database.ACR_SetPersistentInt(ModuleObject, "ACR_HEALTHMONITOR_VAULT_STATUS", (int)NewVaultStatus);

                VaultStatus = NewVaultStatus;
            }


            //
            // If backoff for the GameObjUpdate time is enabled, then try and
            // adjust module performance.
            //

            if (GetLocalInt(ModuleObject, "ACR_HEALTHMONITOR_GAMEOBJUPDATE_BACKOFF") != FALSE)
            {
                int  GameObjUpdateTime = SystemInfo.GetGameObjUpdateTime(this);
                bool SetTime           = false;
                uint Now = (uint)Environment.TickCount;

                switch (HealthStatus)
                {
                case LATENCY_HEALTH_STATUS.Healthy:
                    //
                    // If we are healthy, then keep adjusting the update
                    // time downwards until we hit the default value.  This
                    // trades performance for responsiveness.
                    //

                    if (GameObjUpdateTime != SystemInfo.DEFAULT_GAMEOBJUPDATE_TIME)
                    {
                        if (GameObjUpdateAdjustDelay == 0)
                        {
                            LastLatencyAdjustTick    = Now;
                            GameObjUpdateAdjustDelay = LatencyAdjustHysteresis.GetNextDelayTime();

                            WriteTimestampedLogEntry(String.Format("ACR_HealthMonitor.ProcessMeasuredLatency(): Queued downwards GameObjUpdateTime adjustment in {0} milliseconds (current update time value is {0})...", GameObjUpdateAdjustDelay, GameObjUpdateTime));
                        }
                    }
                    break;

                case LATENCY_HEALTH_STATUS.Warning:
                    //
                    // Don't take any action if we are in the warning
                    // state; instead, keep the current update time where
                    // it is right now, to help avoid thrashing.
                    //

                    break;

                case LATENCY_HEALTH_STATUS.Unhealthy:
                    //
                    // If we are unhealthy, then keep adjusting the update
                    // time upwards until we hit the maximum value.  This
                    // trades responsiveness for performance.
                    //

                    if (GameObjUpdateTime != SystemInfo.MAX_RECOMMENDED_GAMEOBJUPDATE_TIME)
                    {
                        SetTime            = true;
                        GameObjUpdateTime += GAMEOBJUPDATE_ADJUSTMENT;
                    }
                    break;
                }

                if (GameObjUpdateAdjustDelay != 0 &&
                    (Now - LastLatencyAdjustTick) >= GameObjUpdateAdjustDelay &&
                    GameObjUpdateTime != SystemInfo.DEFAULT_GAMEOBJUPDATE_TIME &&
                    HealthStatus == LATENCY_HEALTH_STATUS.Healthy)
                {
                    //
                    // We have passed the required minimum time for a downwards
                    // latency adjustment, effect it now.
                    //

                    SetTime                  = true;
                    GameObjUpdateTime       -= GAMEOBJUPDATE_ADJUSTMENT;
                    GameObjUpdateAdjustDelay = 0;
                }

                if (SetTime)
                {
                    WriteTimestampedLogEntry(String.Format("ACR_HealthMonitor.ProcessMeasuredLatency(): Adjusting GameObjUpdateTime to {0}...", GameObjUpdateTime));
                    SystemInfo.SetGameObjUpdateTime(this, GameObjUpdateTime);
                }
            }
        }