/// <summary>
        /// Queue initial heartbeats to active servers
        /// </summary>
        public void QueueInitialHeartbeats()
        {
            DateTime Now = DateTime.UtcNow;
            uint     HeartbeatsStarted = 0;

            lock (ActiveServerTable)
            {
                foreach (var Pair in ActiveServerTable)
                {
                    NWGameServer Server = Pair.Value;

                    lock (Server)
                    {
                        if (!Server.Online)
                        {
                            continue;
                        }

                        Server.InitialHeartbeat = true;
                        Server.StartHeartbeat();
                        HeartbeatsStarted += 1;
                    }
                }
            }

            Logger.Log(LogLevel.Normal, "NWServerTracker.QueueInitialHeartbeats(): Queued {0} initial server heartbeat requests.", HeartbeatsStarted);

            PendingGameServersSweepTimer.Start();
            ScavengerSweepTimer.Start();
            BlacklistSweepTimer.Start();
        }
        /// <summary>
        /// This method disables future heartbeats and flushes any servers that
        /// may be in the heartbeat path out of that code path before further
        /// forward progress is allowed.
        /// </summary>
        public void DrainHeartbeats()
        {
            //
            // Prevent new requestors from spinning up new heartbeat requests.
            //

            HeartbeatsEnabled = false;

            //
            // Flush any in-flight requestors out of the heartbeat path by
            // ensuring that they have released synchronization.
            //

            Monitor.Enter(HeartbeatLock);
            Monitor.Exit(HeartbeatLock);

            //
            // Stop all of the active timers.  New timers that have elapsed
            // will have already completed starting the timer again or will now
            // observe that future timer restarts are already forbidden.
            //

            PendingGameServersSweepTimer.Stop();
            ScavengerSweepTimer.Stop();
            BlacklistSweepTimer.Stop();
        }
        /// <summary>
        /// This timer callback runs when the scavenge sweep timer elapses.
        /// Its purpose is to cycle through offline game_servers records in the
        /// database, which have had a heartbeat more recent than
        /// ScavengerTimeSpan since the current time.  Such servers are then
        /// re-pinged in a low frequency interval so as to allow servers that
        /// had gone offline for a long period of time (but had been returned
        /// to service afterwards) to be automatically re-listed eventually.
        /// </summary>
        /// <param name="sender">Unused.</param>
        /// <param name="e">Unused.</param>
        private void ScavengerSweepTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            try
            {
                bool     Processed = false;
                DateTime Now       = DateTime.UtcNow;
                string   Query     = String.Format(
                    @"SELECT `game_server_id`,
    `server_address` 
FROM `game_servers` 
WHERE `product_id` = {0} 
AND `game_server_id` > {1}
AND `online` = false 
AND `last_heartbeat` >= '{2}' 
GROUP BY `game_server_id` 
ORDER BY `game_server_id` 
LIMIT 50",
                    MasterServer.ProductID,
                    ScavengeServerId,
                    MasterServer.DateToSQLDate(Now.Subtract(ScavengerTimeSpan)));

                //
                // Examine each server record returned and, if the server is
                // still offline, enqueue a ping to it.  Advance the iterator
                // to the next server as each server is processed.
                //

                using (MySqlDataReader Reader = MasterServer.ExecuteQuery(Query))
                {
                    while (Reader.Read())
                    {
                        uint       ServerId      = Reader.GetUInt32(0);
                        IPEndPoint ServerAddress = ConvertServerHostnameToIPEndPoint(Reader.GetString(1));

                        if (ServerId > ScavengeServerId)
                        {
                            ScavengeServerId = ServerId;
                        }

                        Processed = true;

                        if (ServerAddress == null)
                        {
                            continue;
                        }

                        NWGameServer Server = LookupServerByAddress(ServerAddress, false);

                        if (Server == null || Server.Online == false)
                        {
                            MasterServer.SendServerInfoRequest(ServerAddress);
                        }
                    }
                }

                //
                // If no servers were processed, then the end of the iterator
                // list must have been reached.  Re-start the sweep cycle at
                // the beginning next time.
                //

                if (Processed == false)
                {
                    ScavengeServerId = 0;
                }
            }
            catch (Exception ex)
            {
                Logger.Log(LogLevel.Error, "NWServerTracker.ScavengeSweepTimer_Elapsed(): Excepton processing scavenge server list: {0}", ex);
            }

            lock (HeartbeatLock)
            {
                if (!HeartbeatsEnabled)
                {
                    return;
                }

                ScavengerSweepTimer.Start();
            }
        }