public void Execute(SlashCommand command) { try { Entities.Merge merge = Slack.MessageParser.ForMerge(command); merge.Verify(); Entities.Queue queue = new Entities.Queue() { MaxWokers = 1, Name = merge.Target }; queueBLL.Insert(queue); long enqueuedCount = Hangfire.JobStorage.Current.GetMonitoringApi().EnqueuedCount(queue.Name); IBackgroundJobClient client = new BackgroundJobClient(); IState state = new EnqueuedState(queue.Name); client.Create<BusinessLogic.Merge.Base.ITrigger>(x => x.Execute(command), state); if(enqueuedCount > 0) { slackBLL.DirectMessage(command, $"There {(enqueuedCount > 1 ? "are" : "is")} {enqueuedCount} {(enqueuedCount > 1 ? "merges" : "merge")} ahead of yours."); } } catch(ArgumentException ex) { slackBLL.DirectMessage(command, $"Failed to schedule merge: {ex.Message}"); } }
public void SetQueue_ThrowsAnException_WhenValueIsNotInAGivenFormat() { var state = new EnqueuedState(); Assert.Throws<ArgumentException>(() => state.Queue = "UppercaseLetters"); Assert.Throws<ArgumentException>(() => state.Queue = "punctuation:un-allowed"); Assert.Throws<ArgumentException>(() => state.Queue = "моя_твоя_непонимать"); }
public void SetQueue_DoesNotThrowException_WhenValueIsInACorrectFormat() { var state = new EnqueuedState(); Assert.DoesNotThrow(() => state.Queue = "lowercasedcharacters"); Assert.DoesNotThrow(() => state.Queue = "underscores_allowed"); Assert.DoesNotThrow(() => state.Queue = "1234567890_allowed"); }
public void SerializeData_ReturnsCorrectData() { var state = new EnqueuedState(); var serializedData = state.SerializeData(); Assert.Equal(state.Queue, serializedData["Queue"]); Assert.Equal(JobHelper.SerializeDateTime(state.EnqueuedAt), serializedData["EnqueuedAt"]); }
public void CreateInEnqueuedState() { #region EnqueuedState #1 var client = new BackgroundJobClient(); var state = new EnqueuedState("critical"); // Use the "critical" queue client.Create(() => Console.WriteLine("Hello!"), state); #endregion }
public void ChangeToEnqueuedState() { var jobId = "someid"; #region EnqueuedState #2 var client = new BackgroundJobClient(); var state = new EnqueuedState(); // Use the default queue client.ChangeState(jobId, state, FailedState.StateName); #endregion }
public void IgnoreExceptions_ReturnsFalse() { var state = new EnqueuedState(); Assert.False(state.IgnoreJobLoadException); }
public BackgroundJob Trigger(string recurringJobId) { if (recurringJobId == null) throw new ArgumentNullException(nameof(recurringJobId)); using (var connection = _storage.GetConnection()) { var hash = connection.GetAllEntriesFromHash($"recurring-job:{recurringJobId}"); if (hash == null) { return null; } var job = JobHelper.FromJson<InvocationData>(hash["Job"]).Deserialize(); var state = new EnqueuedState { Reason = "Triggered using recurring job manager" }; if (hash.ContainsKey("Queue")) { state.Queue = hash["Queue"]; } var context = new CreateContext(_storage, connection, job, state); context.Parameters["RecurringJobId"] = recurringJobId; return _factory.Create(context); } }
public void IsFinal_ReturnsFalse() { var state = new EnqueuedState(); Assert.False(state.IsFinal); }
private void TryScheduleJob( JobStorage storage, IStorageConnection connection, string recurringJobId, IReadOnlyDictionary<string, string> recurringJob) { var serializedJob = JobHelper.FromJson<InvocationData>(recurringJob["Job"]); var job = serializedJob.Deserialize(); var cron = recurringJob["Cron"]; var cronSchedule = CrontabSchedule.Parse(cron); try { var timeZone = recurringJob.ContainsKey("TimeZoneId") ? TimeZoneInfo.FindSystemTimeZoneById(recurringJob["TimeZoneId"]) : TimeZoneInfo.Utc; var nowInstant = _instantFactory(cronSchedule, timeZone); var changedFields = new Dictionary<string, string>(); var lastInstant = GetLastInstant(recurringJob, nowInstant); if (nowInstant.GetNextInstants(lastInstant).Any()) { var state = new EnqueuedState { Reason = "Triggered by recurring job scheduler" }; if (recurringJob.ContainsKey("Queue") && !String.IsNullOrEmpty(recurringJob["Queue"])) { state.Queue = recurringJob["Queue"]; } var context = new CreateContext(storage, connection, job, state); context.Parameters["RecurringJobId"] = recurringJobId; var backgroundJob = _factory.Create(context); var jobId = backgroundJob?.Id; if (String.IsNullOrEmpty(jobId)) { Logger.Debug($"Recurring job '{recurringJobId}' execution at '{nowInstant.NowInstant}' has been canceled."); } changedFields.Add("LastExecution", JobHelper.SerializeDateTime(nowInstant.NowInstant)); changedFields.Add("LastJobId", jobId ?? String.Empty); } // Fixing old recurring jobs that doesn't have the CreatedAt field if (!recurringJob.ContainsKey("CreatedAt")) { changedFields.Add("CreatedAt", JobHelper.SerializeDateTime(nowInstant.NowInstant)); } changedFields.Add("NextExecution", nowInstant.NextInstant.HasValue ? JobHelper.SerializeDateTime(nowInstant.NextInstant.Value) : null); connection.SetRangeInHash( $"recurring-job:{recurringJobId}", changedFields); } #if NETFULL catch (TimeZoneNotFoundException ex) { #else catch (Exception ex) { // https://github.com/dotnet/corefx/issues/7552 if (!ex.GetType().Name.Equals("TimeZoneNotFoundException")) throw; #endif Logger.ErrorException( $"Recurring job '{recurringJobId}' was not triggered: {ex.Message}.", ex); } }
private void TryScheduleJob( JobStorage storage, IStorageConnection connection, string recurringJobId, IReadOnlyDictionary<string, string> recurringJob) { var serializedJob = JobHelper.FromJson<InvocationData>(recurringJob["Job"]); var job = serializedJob.Deserialize(); var cron = recurringJob["Cron"]; var cronSchedule = CrontabSchedule.Parse(cron); try { var timeZone = recurringJob.ContainsKey("TimeZoneId") ? TimeZoneInfo.FindSystemTimeZoneById(recurringJob["TimeZoneId"]) : TimeZoneInfo.Utc; var instant = _instantFactory(cronSchedule, timeZone); var lastExecutionTime = recurringJob.ContainsKey("LastExecution") ? JobHelper.DeserializeDateTime(recurringJob["LastExecution"]) : (DateTime?)null; var changedFields = new Dictionary<string, string>(); const string lastJobId = "LastJobId"; if (recurringJob.ContainsKey(lastJobId) && !string.IsNullOrWhiteSpace(recurringJob[lastJobId])) { var jobDetails = storage.GetMonitoringApi().JobDetails(recurringJob[lastJobId]); var finalStates = new[] {"Succeeded", "Deleted"}; if (!jobDetails.History.Select(x => x.StateName).Any(finalStates.Contains)) { Logger.Info("The recurring task " + recurringJobId + " is still running, do not schedule it more. "); return; } } if (instant.GetNextInstants(lastExecutionTime).Any()) { var state = new EnqueuedState { Reason = "Triggered by recurring job scheduler" }; if (recurringJob.ContainsKey("Queue") && !String.IsNullOrEmpty(recurringJob["Queue"])) { state.Queue = recurringJob["Queue"]; } var backgroundJob = _factory.Create(new CreateContext(storage, connection, job, state)); var jobId = backgroundJob != null ? backgroundJob.Id : null; if (String.IsNullOrEmpty(jobId)) { Logger.DebugFormat( "Recurring job '{0}' execution at '{1}' has been canceled.", recurringJobId, instant.NowInstant); } changedFields.Add("LastExecution", JobHelper.SerializeDateTime(instant.NowInstant)); changedFields.Add(lastJobId, jobId ?? String.Empty); } changedFields.Add("NextExecution", JobHelper.SerializeDateTime(instant.NextInstant)); connection.SetRangeInHash( String.Format("recurring-job:{0}", recurringJobId), changedFields); } catch (TimeZoneNotFoundException ex) { Logger.ErrorException( String.Format("Recurring job '{0}' was not triggered: {1}.", recurringJobId, ex.Message), ex); } }
private void TryScheduleJob(IStorageConnection connection, string recurringJobId, Dictionary<string, string> recurringJob) { var serializedJob = JobHelper.FromJson<InvocationData>(recurringJob["Job"]); var job = serializedJob.Deserialize(); var cron = recurringJob["Cron"]; var cronSchedule = CrontabSchedule.Parse(cron); var instant = _instantFactory.GetInstant(cronSchedule); var lastExecutionTime = recurringJob.ContainsKey("LastExecution") ? JobHelper.DeserializeDateTime(recurringJob["LastExecution"]) : (DateTime?)null; if (instant.GetMatches(lastExecutionTime).Any()) { var state = new EnqueuedState { Reason = "Triggered by recurring job scheduler" }; var jobId = _client.Create(job, state); connection.SetRangeInHash( String.Format("recurring-job:{0}", recurringJobId), new Dictionary<string, string> { { "LastExecution", JobHelper.SerializeDateTime(instant.UtcTime) }, { "LastJobId", jobId }, }); } connection.SetRangeInHash( String.Format("recurring-job:{0}", recurringJobId), new Dictionary<string, string> { { "NextExecution", JobHelper.SerializeDateTime(instant.NextOccurrence) } }); }
public void SetQueue_ThrowsAnException_WhenQueueValueIsEmpty() { var state = new EnqueuedState(); Assert.Throws<ArgumentNullException>(() => state.Queue = String.Empty); }
public void Trigger([NotNull] string recurringJobId) { if (recurringJobId == null) throw new ArgumentNullException("recurringJobId"); using (var connection = _storage.GetConnection()) { var hash = connection.GetAllEntriesFromHash(String.Format("recurring-job:{0}", recurringJobId)); if (hash == null) { return; } var job = JobHelper.FromJson<InvocationData>(hash["Job"]).Deserialize(); var state = new EnqueuedState { Reason = "Triggered" }; _client.Create(job, state); } }
private bool EnqueueNextScheduledJob() { using (var connection = _storage.GetConnection()) { var timestamp = JobHelper.ToTimestamp(DateTime.UtcNow); // TODO: it is very slow. Add batching. var jobId = connection .GetFirstByLowestScoreFromSet("schedule", 0, timestamp); if (String.IsNullOrEmpty(jobId)) { return false; } var stateMachine = _stateMachineFactory.Create(connection); var enqueuedState = new EnqueuedState { Reason = "Enqueued as a scheduled job" }; stateMachine.TryToChangeState(jobId, enqueuedState, new[] { ScheduledState.StateName }); return true; } }
private void TryScheduleJob(IStorageConnection connection, string recurringJobId, Dictionary<string, string> recurringJob) { var serializedJob = JobHelper.FromJson<InvocationData>(recurringJob["Job"]); var job = serializedJob.Deserialize(); var cron = recurringJob["Cron"]; var cronSchedule = CrontabSchedule.Parse(cron); var currentTime = _dateTimeProvider.CurrentDateTime; if (recurringJob.ContainsKey("NextExecution")) { var nextExecution = JobHelper.DeserializeDateTime(recurringJob["NextExecution"]); if (nextExecution <= currentTime) { var state = new EnqueuedState { Reason = "Triggered by recurring job scheduler" }; var jobId = _client.Create(job, state); connection.SetRangeInHash( String.Format("recurring-job:{0}", recurringJobId), new Dictionary<string, string> { { "LastExecution", JobHelper.SerializeDateTime(currentTime) }, { "LastJobId", jobId }, { "NextExecution", JobHelper.SerializeDateTime(_dateTimeProvider.GetNextOccurrence(cronSchedule)) } }); } } else { var nextExecution = _dateTimeProvider.GetNextOccurrence(cronSchedule); connection.SetRangeInHash( String.Format("recurring-job:{0}", recurringJobId), new Dictionary<string, string> { { "NextExecution", JobHelper.SerializeDateTime(nextExecution) } }); } }
public void Trigger([NotNull] string recurringJobId) { if (recurringJobId == null) throw new ArgumentNullException("recurringJobId"); using (var connection = _storage.GetConnection()) { var hash = connection.GetAllEntriesFromHash(String.Format("recurring-job:{0}", recurringJobId)); if (hash == null) { return; } var job = JobHelper.FromJson<InvocationData>(hash["Job"]).Deserialize(); var state = new EnqueuedState { Reason = "Triggered using recurring job manager" }; if (hash.ContainsKey("Queue")) { state.Queue = hash["Queue"]; } _factory.Create(new CreateContext(_storage, connection, job, state)); } }
private bool EnqueueNextScheduledJob() { using (var connection = _storage.GetConnection()) using (connection.AcquireDistributedLock("locks:schedulepoller", DefaultLockTimeout)) { var timestamp = JobHelper.ToTimestamp(DateTime.UtcNow); // TODO: it is very slow. Add batching. var jobId = connection .GetFirstByLowestScoreFromSet("schedule", 0, timestamp); if (String.IsNullOrEmpty(jobId)) { // No more scheduled jobs pending. return false; } var stateMachine = _stateMachineFactory.Create(connection); var enqueuedState = new EnqueuedState { Reason = "Triggered scheduled job" }; if (!stateMachine.ChangeState(jobId, enqueuedState, new[] { ScheduledState.StateName })) { // When state change does not succeed, this means that background job // was in a state other than Scheduled, or it was moved to a state other // than Enqueued. We should remove the job identifier from the set in // the first case only, but can't differentiate these cases yet. using (var transaction = connection.CreateWriteTransaction()) { transaction.RemoveFromSet("schedule", jobId); transaction.Commit(); } } return true; } }
private void TryScheduleJob( JobStorage storage, IStorageConnection connection, string recurringJobId, IReadOnlyDictionary<string, string> recurringJob) { var serializedJob = JobHelper.FromJson<InvocationData>(recurringJob["Job"]); var job = serializedJob.Deserialize(); var cron = recurringJob["Cron"]; var cronSchedule = CrontabSchedule.Parse(cron); try { var timeZone = recurringJob.ContainsKey("TimeZoneId") ? TimeZoneInfo.FindSystemTimeZoneById(recurringJob["TimeZoneId"]) : TimeZoneInfo.Utc; var nowInstant = _instantFactory(cronSchedule, timeZone); var changedFields = new Dictionary<string, string>(); var lastInstant = GetLastInstant(recurringJob, nowInstant); if (nowInstant.GetNextInstants(lastInstant).Any()) { var state = new EnqueuedState { Reason = "Triggered by recurring job scheduler" }; if (recurringJob.ContainsKey("Queue") && !String.IsNullOrEmpty(recurringJob["Queue"])) { state.Queue = recurringJob["Queue"]; } var backgroundJob = _factory.Create(new CreateContext(storage, connection, job, state)); var jobId = backgroundJob != null ? backgroundJob.Id : null; if (String.IsNullOrEmpty(jobId)) { Logger.DebugFormat( "Recurring job '{0}' execution at '{1}' has been canceled.", recurringJobId, nowInstant.NowInstant); } changedFields.Add("LastExecution", JobHelper.SerializeDateTime(nowInstant.NowInstant)); changedFields.Add("LastJobId", jobId ?? String.Empty); } // Fixing old recurring jobs that doesn't have the CreatedAt field if (!recurringJob.ContainsKey("CreatedAt")) { changedFields.Add("CreatedAt", JobHelper.SerializeDateTime(nowInstant.NowInstant)); } changedFields.Add("NextExecution", nowInstant.NextInstant.HasValue ? JobHelper.SerializeDateTime(nowInstant.NextInstant.Value) : null); connection.SetRangeInHash( String.Format("recurring-job:{0}", recurringJobId), changedFields); } catch (TimeZoneNotFoundException ex) { Logger.ErrorException( String.Format("Recurring job '{0}' was not triggered: {1}.", recurringJobId, ex.Message), ex); } }
public void StateName_IsCorrect() { var state = new EnqueuedState(); Assert.Equal(EnqueuedState.StateName, state.Name); }
private void TryScheduleJob(IStorageConnection connection, string recurringJobId, Dictionary<string, string> recurringJob) { var serializedJob = JobHelper.FromJson<InvocationData>(recurringJob["Job"]); var job = serializedJob.Deserialize(); var cron = recurringJob["Cron"]; var parts = cron.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); var cronSchedule = CrontabSchedule.Parse(cron, new CrontabSchedule.ParseOptions { IncludingSeconds = (parts.Length >= 6) }); try { var timeZone = recurringJob.ContainsKey("TimeZoneId") ? TimeZoneInfo.FindSystemTimeZoneById(recurringJob["TimeZoneId"]) : TimeZoneInfo.Utc; var instant = _instantFactory.GetInstant(cronSchedule, timeZone); var lastExecutionTime = recurringJob.ContainsKey("LastExecution") ? JobHelper.DeserializeDateTime(recurringJob["LastExecution"]) : (DateTime?)null; var changedFields = new Dictionary<string, string>(); if (instant.GetNextInstants(lastExecutionTime).Any()) { var state = new EnqueuedState { Reason = "Triggered by recurring job scheduler" }; var jobId = _client.Create(job, state); if (String.IsNullOrEmpty(jobId)) { Logger.DebugFormat( "Recurring job '{0}' execution at '{1}' has been canceled.", recurringJobId, instant.NowInstant); } changedFields.Add("LastExecution", JobHelper.SerializeDateTime(instant.NowInstant)); changedFields.Add("LastJobId", jobId ?? String.Empty); } changedFields.Add("NextExecution", JobHelper.SerializeDateTime(instant.NextInstant)); connection.SetRangeInHash( String.Format("recurring-job:{0}", recurringJobId), changedFields); } catch (TimeZoneNotFoundException ex) { Logger.ErrorException( String.Format("Recurring job '{0}' was not triggered: {1}.", recurringJobId, ex.Message), ex); } }
public void Ctor_ShouldSetQueue_WhenItWasGiven() { var state = new EnqueuedState("critical"); Assert.Equal("critical", state.Queue); }