Thread getting stuck in HttpApplication

183 views Asked by At

I have a WCF application that starts from Global.asax file, there is an application start event, the code is as follows:

    public class Global : HttpApplication
    {
        private bool isInitialized = false;
        private static readonly ILog log = LogManager.GetLogger(typeof(Global));
        private PushController pushController;
        private Task pushMonitoringTask;

        protected void Application_Start(object sender, EventArgs e)
        {
            Initialize();
            pushMonitoringTask = Task.Run(() => pushController.StartMonitoring());
        }
 ...}

The push controller:

    public class PushController
    {
        private IRepository<Question> QuestionRepository { get; set; }
        private IRepository<Mobile_Report> ReportRepository { get; set; }

        private readonly string _syncFilePath;

        private readonly string certificatePath;

        private readonly PushBroker _broker;

        private bool _isStopRequired;

        public PushController(IRepository<Question> questionRepository, IRepository<Mobile_Report> reportsRepository, HttpServerUtility server)
        {
            QuestionRepository = questionRepository;
            ReportRepository = reportsRepository;

            _broker = new PushBroker();

            _syncFilePath = server.MapPath("~/SyncFile.txt");
            certificatePath = "My cert path";

            if (!File.Exists(_syncFilePath))
            {
                using (StreamWriter sw = File.AppendText(_syncFilePath))
                {
                    sw.WriteLine(DateTime.Now);
                    sw.Flush();
                }
            }

            _isStopRequired = false;
        }

        public void StartMonitoring()
        {
            var delay = Convert.ToInt32(ConfigurationManager.AppSettings["NewQuestionsMonitoringDelay"]);
            RunPushServices();

            while (!_isStopRequired)
            {
                using (var fileStream = new FileStream(_syncFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
                {
                    DateTime lastSyncDate;

                    using (var sw = new StreamReader(fileStream))
                    {
                        lastSyncDate = Convert.ToDateTime(sw.ReadLine());
                    }

                    if (lastSyncDate == default(DateTime))
                    {
                        throw new Exception("Corrupted or missing sync date");
                    }

                    EnqueueNotificationsList(lastSyncDate);

                    using (var sw = new StreamWriter(_syncFilePath, false))
                    {
                        sw.WriteLine(DateTime.Now);
                        sw.Flush();
                    }
                }

                Thread.Sleep(delay * 1000);
            }

            //_broker.StopAllServices();//waits for the queue to drain
        }

        private void RunPushServices()
        {
            _broker.RegisterAppleService(new ApplePushChannelSettings(false, certificatePath, "My cert password"));
            //_broker.RegisterGcmService(new GcmPushChannelSettings("senderId", "sender auth token", "android app package name"));
        }

        private void EnqueueNotificationsList(DateTime lastSyncDate)
        {
            var newQuestions = QuestionRepository.GetAll().Where(q => q.UtcDateCreated >= lastSyncDate);
            var newReports = ReportRepository.GetAll().Where(r => r.CreatedDate >= lastSyncDate);

            EnqueueQuestionsNotifications(newQuestions);
            EnqueueReportsNorifications(newReports);
        }

        private void EnqueueQuestionsNotifications(IEnumerable<Question> newQuestions)
        {
            foreach (var questionGroup in newQuestions.GroupBy(q => q.Mobile_Sample))
            {
                var firstQuestion = questionGroup.First();
                var targetSample = firstQuestion.Mobile_Sample;
                var allSessions = firstQuestion.Mobile_Sample.Mobile_Claim.Mobile_Contact.UserSession;

                var questionsNotificationString = BuildQuestionsNotificationString(targetSample, questionGroup.Count());

                foreach (var userSession in allSessions)
                {
                    switch (userSession.DeviceType)
                    {
                        case (int)DeviceType.IOSDeivce:
                            _broker.QueueNotification(new AppleNotification()
                                .ForDeviceToken(userSession.DeviceToken)
                                .WithAlert(questionsNotificationString)
                                .WithBadge(1)
                                .WithSound("default")
                                .WithCustomItem("PushInfo", "Questions", targetSample.ID));
                            break;
                        //case (int)DeviceType.AndroidDevice:
                        //    _broker.QueueNotification(
                        //        new GcmNotification().ForDeviceRegistrationId(userSession.DeviceToken)
                        //            .WithJson(@"{""alert"":""ITEL Questions"",""badge"":7,""sound"":""sound.caf""}");
                        //    break;
                    }
                }
            }
        }

        private void EnqueueReportsNorifications(IEnumerable<Mobile_Report> newReports)
        {
            foreach (var reportsGroup in newReports.GroupBy(q => q.Mobile_Sample))
            {
                var firstReport = reportsGroup.First();
                var targetSample = firstReport.Mobile_Sample;
                var allSessions = firstReport.Mobile_Sample.Mobile_Claim.Mobile_Contact.UserSession;

                var reportNotificationString = BuildReportNotificationString(targetSample);

                foreach (var userSession in allSessions)
                {
                    switch (userSession.DeviceType)
                    {
                        case (int)DeviceType.IOSDeivce:
                            _broker.QueueNotification(new AppleNotification()
                                .ForDeviceToken(userSession.DeviceToken)
                                .WithAlert(reportNotificationString)
                                .WithBadge(1)
                                .WithSound("default")
                                .WithCustomItem("Target", "Reports", targetSample.ID));
                            break;
                        //case (int)DeviceType.AndroidDevice:
                        //    _broker.QueueNotification(
                        //        new GcmNotification().ForDeviceRegistrationId(userSession.DeviceToken)
                        //            .WithJson(@"{""alert"":""ITEL Questions"",""badge"":7,""sound"":""sound.caf""}");
                        //    break;
                    }
                }
            }
        }

        private string BuildQuestionsNotificationString(Mobile_Sample targetSample, int count)
        {
            var claimLastNameString = !string.IsNullOrEmpty(targetSample.Mobile_Claim.IILastName)
                ? " " + targetSample.Mobile_Claim.IILastName
                : string.Empty;


            var claimNumberString = !string.IsNullOrEmpty(targetSample.Mobile_Claim.ClaimNumber)
                ? " Claim #" + targetSample.Mobile_Claim.ClaimNumber + ", "
                : " Claim # -";

            var areaDamagedString = !string.IsNullOrEmpty(targetSample.AreaDamaged)
                ? " (" + targetSample.AreaDamaged + ")"
                : string.Empty;

            return "You have " + count + " new questions for" + claimLastNameString + claimNumberString + targetSample.Mobile_SampleType.Name + areaDamagedString;
        }

        private string BuildReportNotificationString(Mobile_Sample targetSample)
        {
            var claimLastNameString = !string.IsNullOrEmpty(targetSample.Mobile_Claim.IILastName)
                ? " " + targetSample.Mobile_Claim.IILastName
                : string.Empty;


            var claimNumberString = !string.IsNullOrEmpty(targetSample.Mobile_Claim.ClaimNumber)
                ? " Claim #" + targetSample.Mobile_Claim.ClaimNumber + ", "
                : " Claim # -";

            var areaDamagedString = !string.IsNullOrEmpty(targetSample.AreaDamaged)
                ? " (" + targetSample.AreaDamaged + ")"
                : string.Empty;

            return "You have a new report for" + claimLastNameString + claimNumberString + targetSample.Mobile_SampleType.Name + areaDamagedString;
        }

        public void RequestMonitoringStop()
        {
            _isStopRequired = true;
        }
    }

so as you can see I'm literally launching a background task that terminates on Application_End in Global.asax. The problem is that periodically the thread gets stuck and does not proceed any more (probably, crashes silently or whatever), so the SyncFile does not get updated and no new notifications are pushed to the clients, the main functionality of the server is still running though, also a tricky thing to mention - i can not reproduce the problem locally, it appears only when the server is deployed to the production server, maybe there are some gotchas about tasks in HttpApplication, or am i missing something? Thanks in advance.

1

There are 1 answers

0
HardLuck On

Found the solution - The site goes to "idle" if there are no requests for 20 (by default) minutes, have to edit IIS settings.