예제 #1
0
        public async Task CallWebHooks()
        {
            List <int> callIds = null;

            using (TolkDbContext context = _options.GetContext())
            {
                callIds = await context.OutboundWebHookCalls
                          .Where(e => e.DeliveredAt == null && e.FailedTries < NumberOfTries && !e.IsHandling)
                          .Select(e => e.OutboundWebHookCallId)
                          .ToListAsync();

                if (callIds.Any())
                {
                    var calls = context.OutboundWebHookCalls
                                .Where(e => callIds.Contains(e.OutboundWebHookCallId) && e.IsHandling == false)
                                .Select(c => c);
                    await calls.ForEachAsync(c => c.IsHandling = true);

                    await context.SaveChangesAsync();
                }
            }

            _logger.LogInformation("Found {count} outbound web hook calls to send: {callIds}",
                                   callIds.Count, string.Join(", ", callIds));

            if (callIds.Any())
            {
                var tasks = new List <Task>();
                foreach (var callId in callIds)
                {
                    tasks.Add(Task.Factory.StartNew(() => CallWebhook(callId), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Current));
                }
                await Task.Factory.ContinueWhenAny(tasks.ToArray(), r => { });
            }
        }
예제 #2
0
        public async Task SendEmails()
        {
            using TolkDbContext context = _options.GetContext();
            var emailIds = await context.OutboundEmails
                           .Where(e => e.DeliveredAt == null && !e.IsHandling)
                           .Select(e => e.OutboundEmailId)
                           .ToListAsync();

            _logger.LogInformation("Found {count} emails to send: {emailIds}",
                                   emailIds.Count, string.Join(", ", emailIds));

            if (emailIds.Any())
            {
                try
                {
                    var emails = context.OutboundEmails
                                 .Where(e => emailIds.Contains(e.OutboundEmailId) && e.IsHandling == false)
                                 .Select(c => c);
                    await emails.ForEachAsync(c => c.IsHandling = true);

                    await context.SaveChangesAsync();

                    using var client = new SmtpClient();
                    await client.ConnectAsync(_options.Smtp.Host, _options.Smtp.Port, _options.Smtp.UseAuthentication?SecureSocketOptions.StartTls : SecureSocketOptions.Auto);

                    if (_options.Smtp.UseAuthentication)
                    {
                        await client.AuthenticateAsync(_options.Smtp.UserName, _options.Smtp.Password);
                    }
                    var from = new MailboxAddress(_senderPrepend + Constants.SystemName, _options.Smtp.FromAddress);

                    foreach (var emailId in emailIds)
                    {
                        var email = await context.OutboundEmails
                                    .SingleOrDefaultAsync(e => e.OutboundEmailId == emailId && e.DeliveredAt == null);

                        try
                        {
                            if (email == null)
                            {
                                _logger.LogInformation("Email {emailId} was in list to be sent, but now appears to have been sent.", emailId);
                            }
                            else
                            {
                                email.IsHandling = true;
                                await context.SaveChangesAsync();

                                _logger.LogInformation("Sending email {emailId} to {recipient}", emailId, email.Recipient);

                                await client.SendAsync(email.ToMimeKitMessage(from));

                                email.DeliveredAt = _clock.SwedenNow;
                            }
                        }
                        catch (Exception ex)
                        {
                            _logger.LogError(ex, "Failure sending e-mail {emailId}");
                        }
                        finally
                        {
                            email.IsHandling = false;
                            await context.SaveChangesAsync();
                        }
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Something went wrong when sending emails");
                }
                finally
                {
                    //Making sure no emails are left hanging
                    var emails = context.OutboundEmails
                                 .Where(e => emailIds.Contains(e.OutboundEmailId) && e.IsHandling == true)
                                 .Select(c => c);
                    await emails.ForEachAsync(c => c.IsHandling = false);

                    await context.SaveChangesAsync();
                }
            }
        }
예제 #3
0
        private async void Run(bool isInit = false)
        {
            _logger.LogTrace("EntityScheduler waking up.");

            if ((nextDailyRunTime - _clock.SwedenNow).TotalHours > 25 || nextDailyRunTime.Hour != timeToRun)
            {
                _logger.LogWarning("nextDailyRunTime set to invalid time, was {0}", nextDailyRunTime);
                DateTimeOffset now = _clock.SwedenNow;
                now -= now.TimeOfDay;
                nextDailyRunTime = now - now.TimeOfDay;
                nextDailyRunTime = nextDailyRunTime.AddHours(timeToRun);

                if (_clock.SwedenNow.Hour > timeToRun)
                {
                    // Next remind is tomorrow
                    nextDailyRunTime = nextDailyRunTime.AddDays(1);
                }
            }

            if (isInit)
            {
                using TolkDbContext context = _options.GetContext();
                await context.OutboundEmails.Where(e => e.IsHandling == true)
                .Select(c => c).ForEachAsync(c => c.IsHandling = false);

                await context.OutboundWebHookCalls.Where(e => e.IsHandling == true)
                .Select(c => c).ForEachAsync(c => c.IsHandling = false);

                await context.SaveChangesAsync();
            }

            try
            {
                if (nextRunIsNotifications)
                {
                    //Separate these, to get a better parallellism for the notifications
                    // They fail to run together with the other Continous jobs , due to recurring deadlocks around the email table...
                    List <Task> tasksToRunNotifications = new List <Task>
                    {
                        Task.Factory.StartNew(() => _services.GetRequiredService <EmailService>().SendEmails(), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Current),
                        Task.Factory.StartNew(() => _services.GetRequiredService <WebHookService>().CallWebHooks(), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Current)
                    };
                    await Task.Factory.ContinueWhenAny(tasksToRunNotifications.ToArray(), r => { });
                }
                else
                {
                    //would like to have a timer here, to make it possible to get tighter runs if the last run ran for longer than 10 seconds or somethng...
                    using var serviceScope = _services.CreateScope();
                    Task[] tasksToRun;

                    if (_clock.SwedenNow > nextDailyRunTime)
                    {
                        nextDailyRunTime  = nextDailyRunTime.AddDays(1);
                        nextDailyRunTime -= nextDailyRunTime.TimeOfDay;
                        nextDailyRunTime  = nextDailyRunTime.AddHours(timeToRun);
                        _logger.LogTrace("Running DailyRunTime, next run on {0}", nextDailyRunTime);

                        tasksToRun = new Task[]
                        {
                            RunDailyJobs(serviceScope.ServiceProvider),
                        };
                    }
                    else
                    {
                        tasksToRun = new Task[]
                        {
                            RunContinousJobs(serviceScope.ServiceProvider),
                        };
                    }
                    if (!Task.WaitAll(tasksToRun, allotedTimeAllTasks))
                    {
                        throw new InvalidOperationException($"All tasks instances didn't complete execution within the allotted time: {allotedTimeAllTasks / 1000} seconds");
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogCritical(ex, "Entity Scheduler failed ({message}).", ex.Message);
                _ = _services.GetRequiredService <EmailService>().SendErrorEmail(nameof(EntityScheduler), nameof(Run), ex);
            }
            finally
            {
                nextRunIsNotifications = !nextRunIsNotifications;
                if (_services.GetRequiredService <ITolkBaseOptions>().RunEntityScheduler)
                {
                    await Task.Delay(timeDelayContinousJobs).ContinueWith(t => Run(), TaskScheduler.Default);
                }
            }

            _logger.LogTrace($"EntityScheduler done, scheduled to wake up in {timeDelayContinousJobs / 1000} seconds again");
        }