diff --git a/src/Hangfire.AspNetCore/Hangfire.AspNetCore.csproj b/src/Hangfire.AspNetCore/Hangfire.AspNetCore.csproj index 3dd9d2bb8..90d45a269 100644 --- a/src/Hangfire.AspNetCore/Hangfire.AspNetCore.csproj +++ b/src/Hangfire.AspNetCore/Hangfire.AspNetCore.csproj @@ -1,6 +1,6 @@  - net451;net461;netstandard1.3;netstandard2.0;netcoreapp3.0 + net48;net451;net461;netstandard1.3;netstandard2.0;netcoreapp3.0 portable false true @@ -8,7 +8,7 @@ Hangfire - + full @@ -28,7 +28,7 @@ - + diff --git a/src/Hangfire.Core/Dashboard/Content/resx/Strings.Designer.cs b/src/Hangfire.Core/Dashboard/Content/resx/Strings.Designer.cs index 816480a33..aa4bd2a1a 100644 --- a/src/Hangfire.Core/Dashboard/Content/resx/Strings.Designer.cs +++ b/src/Hangfire.Core/Dashboard/Content/resx/Strings.Designer.cs @@ -8,8 +8,6 @@ // //------------------------------------------------------------------------------ -using System.Reflection; - namespace Hangfire.Dashboard.Resources { using System; @@ -41,7 +39,7 @@ internal Strings() { public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Hangfire.Dashboard.Content.resx.Strings", typeof(Strings).GetTypeInfo().Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Hangfire.Dashboard.Content.resx.Strings", typeof(Strings).Assembly); resourceMan = temp; } return resourceMan; @@ -693,6 +691,15 @@ public static string JobsSidebarMenu_Succeeded { } } + /// + /// Looks up a localized string similar to Suspended. + /// + public static string JobsSidebarMenu_Suspended { + get { + return ResourceManager.GetString("JobsSidebarMenu_Suspended", resourceCulture); + } + } + /// /// Looks up a localized string similar to Back to site. /// @@ -837,6 +844,15 @@ public static string Metrics_SucceededJobs { } } + /// + /// Looks up a localized string similar to Suspended. + /// + public static string Metrics_SuspendedCount { + get { + return ResourceManager.GetString("Metrics_SuspendedCount", resourceCulture); + } + } + /// /// Looks up a localized string similar to Total Connections. /// @@ -1314,5 +1330,41 @@ public static string SucceededJobsPage_Title { return ResourceManager.GetString("SucceededJobsPage_Title", resourceCulture); } } + + /// + /// Looks up a localized string similar to All is OK – you have no suspended.. + /// + public static string SuspendedJobsPage_NoJobs { + get { + return ResourceManager.GetString("SuspendedJobsPage_NoJobs", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Suspended Jobs. + /// + public static string SuspendedJobsPage_Title { + get { + return ResourceManager.GetString("SuspendedJobsPage_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <h4>This page can't be displayed</h4> + /// <p> + /// Don't worry, suspend is working as expected. Your current job storage does not support + /// some queries required to show this page. Please try to update your storage or wait until + /// the full command set is implemented. + /// </p> + /// <p> + /// Please go to the <a href="{0}">Scheduled jobs</a> page to see all the + /// scheduled jobs including retries. + /// </p>. + /// + public static string SuspendedPage_Warning_Html { + get { + return ResourceManager.GetString("SuspendedPage_Warning_Html", resourceCulture); + } + } } } diff --git a/src/Hangfire.Core/Dashboard/Content/resx/Strings.resx b/src/Hangfire.Core/Dashboard/Content/resx/Strings.resx index c9229800d..adb7d85ed 100644 --- a/src/Hangfire.Core/Dashboard/Content/resx/Strings.resx +++ b/src/Hangfire.Core/Dashboard/Content/resx/Strings.resx @@ -545,7 +545,33 @@ Error + + + Suspended + + + Suspended + + + <h4>This page can't be displayed</h4> + <p> + Don't worry, suspend is working as expected. Your current job storage does not support + some queries required to show this page. Please try to update your storage or wait until + the full command set is implemented. + </p> + <p> + Please go to the <a href="{0}">Scheduled jobs</a> page to see all the + scheduled jobs including retries. + </p> + + + All is OK – you have no suspended. + + + Suspended Jobs + Parameters + \ No newline at end of file diff --git a/src/Hangfire.Core/Dashboard/DashboardMetrics.cs b/src/Hangfire.Core/Dashboard/DashboardMetrics.cs index 1e94b4c95..65cf7aff5 100644 --- a/src/Hangfire.Core/Dashboard/DashboardMetrics.cs +++ b/src/Hangfire.Core/Dashboard/DashboardMetrics.cs @@ -62,7 +62,7 @@ public static IEnumerable GetMetrics() } public static readonly DashboardMetric ServerCount = new DashboardMetric( - "servers:count", + "servers:count", "Metrics_Servers", page => new Metric(page.Statistics.Servers) { @@ -198,5 +198,26 @@ public static IEnumerable GetMetrics() Style = awaitingCount > 0 ? MetricStyle.Info : MetricStyle.Default }; }); + public static readonly DashboardMetric SuspendedCount = new DashboardMetric( + "suspended:count", + "Metrics_SuspendedCount", + page => + { + long suspendedCount = -1; + + using (var connection = page.Storage.GetConnection()) + { + var storageConnection = connection as JobStorageConnection; + if (storageConnection != null) + { + suspendedCount = storageConnection.GetSetCount("paused-jobs"); + } + } + + return new Metric(suspendedCount) + { + Style = suspendedCount > 0 ? MetricStyle.Info : MetricStyle.Default + }; + }); } } diff --git a/src/Hangfire.Core/Dashboard/DashboardRoutes.cs b/src/Hangfire.Core/Dashboard/DashboardRoutes.cs index f6ac2d9d6..038ee4784 100644 --- a/src/Hangfire.Core/Dashboard/DashboardRoutes.cs +++ b/src/Hangfire.Core/Dashboard/DashboardRoutes.cs @@ -176,6 +176,16 @@ static DashboardRoutes() Routes.AddRazorPage("/servers", x => new ServersPage()); Routes.AddRazorPage("/retries", x => new RetriesPage()); + Routes.AddRazorPage("/jobs/suspended", x => new SuspendedJobsPage()); + Routes.AddRecurringBatchCommand( + "/recurring/suspend", + (manager, jobId) => manager.SuspendJob(jobId)); + + Routes.AddRecurringBatchCommand( + "/recurring/resume", + (manager, jobId) => manager.ResumeJob(jobId)); + + #endregion } diff --git a/src/Hangfire.Core/Dashboard/JobsSidebarMenu.cs b/src/Hangfire.Core/Dashboard/JobsSidebarMenu.cs index 9a5ed3d92..331177abd 100644 --- a/src/Hangfire.Core/Dashboard/JobsSidebarMenu.cs +++ b/src/Hangfire.Core/Dashboard/JobsSidebarMenu.cs @@ -68,6 +68,12 @@ static JobsSidebarMenu() Active = page.RequestPath.StartsWith("/jobs/awaiting"), Metric = DashboardMetrics.AwaitingCount }); + Items.Add(page => new MenuItem(Strings.JobsSidebarMenu_Suspended, page.Url.To("/jobs/suspended")) + { + Active = page.RequestPath.StartsWith("/jobs/suspended"), + Metric = DashboardMetrics.SuspendedCount + }); + } } } \ No newline at end of file diff --git a/src/Hangfire.Core/Dashboard/Pages/SuspendedJobsPage.cshtml b/src/Hangfire.Core/Dashboard/Pages/SuspendedJobsPage.cshtml new file mode 100644 index 000000000..9d5633f91 --- /dev/null +++ b/src/Hangfire.Core/Dashboard/Pages/SuspendedJobsPage.cshtml @@ -0,0 +1,171 @@ +@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@ +@using System +@using System.Collections.Generic +@using Hangfire +@using Hangfire.Common +@using Hangfire.Dashboard +@using Hangfire.Dashboard.Pages +@using Hangfire.Dashboard.Resources +@using Hangfire.Storage +@inherits RazorPage +@{ + Layout = new LayoutPage(Strings.SuspendedJobsPage_Title); + + int from, perPage; + + int.TryParse(Query("from"), out from); + int.TryParse(Query("count"), out perPage); + + Pager pager = null; + List jobIds = null; + + using (var connection = Storage.GetConnection()) + { + var storageConnection = connection as JobStorageConnection; + + if (storageConnection != null) + { + pager = new Pager(@from, perPage, storageConnection.GetSetCount("paused-jobs")); + jobIds = storageConnection.GetRangeFromSet("retries", pager.FromRecord, pager.FromRecord + pager.RecordsPerPage - 1); + } + } +} + +@if (pager == null) +{ +
+ @Html.Raw(String.Format(Strings.SuspendedPage_Warning_Html, Url.To("/jobs/suspended"))) +
+} +else +{ +
+
+ @Html.JobsSidebar() +
+
+

@Strings.SuspendedJobsPage_Title

+ @if (jobIds.Count == 0) + { +
+ @Strings.SuspendedJobsPage_NoJobs +
+ } + else + { +
+
+ @if (!IsReadOnly) + { + + } + @if (!IsReadOnly) + { + + + @**@ + } + @Html.PerPageSelector(pager) +
+ +
+ + + + @if (!IsReadOnly) + { + + } + + + + + + + + + + @foreach (var jobId in jobIds) + { + JobData jobData; + StateData stateData; + string _jobId = jobId; + using (var connection = Storage.GetConnection()) + { + var recurringJob = connection.GetRecurringJob(jobId, new DefaultTimeZoneResolver(), DateTime.UtcNow); + if (recurringJob != null) + { + _jobId = recurringJob.LastJobId; + } + jobData = connection.GetJobData(_jobId); + stateData = connection.GetStateData(_jobId); + } + + + @if (!IsReadOnly) + { + + } + + @if (jobData == null) + { + + } + else + { + + + + + + } + + } + +
+ + @Strings.Common_Id@Strings.Common_State@Strings.Common_Job@Strings.Common_Reason@Strings.Common_Retry@Strings.Common_Created
+ + + @Html.JobIdLink(_jobId) + Job expired. + @Html.StateLabel(jobData.State) + + @Html.JobNameLink(_jobId, jobData.Job) + + @(stateData?.Reason) + + @if (stateData != null && stateData.Data.ContainsKey("EnqueueAt")) + { + @Html.RelativeTime(JobHelper.DeserializeDateTime(stateData.Data["EnqueueAt"])) + } + + @Html.RelativeTime(jobData.CreatedAt) +
+
+ + @Html.Paginator(pager) +
+ } +
+
+} \ No newline at end of file diff --git a/src/Hangfire.Core/Dashboard/Pages/SuspendedJobsPage.cshtml.cs b/src/Hangfire.Core/Dashboard/Pages/SuspendedJobsPage.cshtml.cs new file mode 100644 index 000000000..6d588f44f --- /dev/null +++ b/src/Hangfire.Core/Dashboard/Pages/SuspendedJobsPage.cshtml.cs @@ -0,0 +1,648 @@ +#pragma warning disable 1591 +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Hangfire.Dashboard.Pages +{ + + #line 2 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + using System; + + #line default + #line hidden + + #line 3 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + using System.Collections.Generic; + + #line default + #line hidden + using System.Linq; + using System.Text; + + #line 4 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + using Hangfire; + + #line default + #line hidden + + #line 5 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + using Hangfire.Common; + + #line default + #line hidden + + #line 6 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + using Hangfire.Dashboard; + + #line default + #line hidden + + #line 7 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + using Hangfire.Dashboard.Pages; + + #line default + #line hidden + + #line 8 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + using Hangfire.Dashboard.Resources; + + #line default + #line hidden + + #line 9 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + using Hangfire.Storage; + + #line default + #line hidden + + [System.CodeDom.Compiler.GeneratedCodeAttribute("RazorGenerator", "2.0.0.0")] + internal partial class SuspendedJobsPage : RazorPage + { +#line hidden + + public override void Execute() + { + + +WriteLiteral("\r\n"); + + + + + + + + + + + + + #line 11 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + + Layout = new LayoutPage("Suspended Jobs"); + + int from, perPage; + + int.TryParse(Query("from"), out from); + int.TryParse(Query("count"), out perPage); + + Pager pager = null; + List jobIds = null; + + using (var connection = Storage.GetConnection()) + { + var storageConnection = connection as JobStorageConnection; + + if (storageConnection != null) + { + pager = new Pager(@from, perPage, storageConnection.GetSetCount("paused-jobs")); + jobIds = storageConnection.GetRangeFromSet("paused-jobs", pager.FromRecord, pager.FromRecord + pager.RecordsPerPage - 1); + } + } + + + + #line default + #line hidden +WriteLiteral("\r\n"); + + + + #line 34 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + if (pager == null) +{ + + + #line default + #line hidden +WriteLiteral("
\r\n "); + + + + #line 37 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + Write(Html.Raw(String.Format(Strings.RetriesPage_Warning_Html, Url.To("/jobs/scheduled")))); + + + #line default + #line hidden +WriteLiteral("\r\n
\r\n"); + + + + #line 39 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" +} +else +{ + + + #line default + #line hidden +WriteLiteral("
\r\n
\r\n

"); + + + + #line 44 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + Write("Suspended Jobs"); + + + #line default + #line hidden +WriteLiteral("

\r\n"); + + + + #line 45 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + if (jobIds.Count == 0) + { + + + #line default + #line hidden +WriteLiteral("
\r\n "); + + + + #line 48 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + Write("No Suspended Jobs"); + + + #line default + #line hidden +WriteLiteral("\r\n
\r\n"); + + + + #line 50 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + } + else + { + + + #line default + #line hidden +WriteLiteral("
\r\n
\r\n"); + + + + #line 55 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + if (!IsReadOnly) + { + + + #line default + #line hidden +WriteLiteral(" \r\n"); + + + + #line 64 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + } + + + #line default + #line hidden + + + #line 65 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + if (!IsReadOnly) + { + + + #line default + #line hidden +WriteLiteral(" \r\n"); + + + + #line 75 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + } + + + #line default + #line hidden +WriteLiteral(" "); + + + + #line 76 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + Write(Html.PerPageSelector(pager)); + + + #line default + #line hidden +WriteLiteral("\r\n
\r\n\r\n
\r\n \r\n " + +" \r\n \r\n"); + + + + #line 83 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + if (!IsReadOnly) + { + + + #line default + #line hidden +WriteLiteral(" \r\n"); + + + + #line 88 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + } + + + #line default + #line hidden +WriteLiteral(" \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n"); + + + + #line 98 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + foreach (var jobId in jobIds) + { + JobData jobData; + StateData stateData; + string _jobId = jobId; + using (var connection = Storage.GetConnection()) + { + var recurringJob = connection.GetRecurringJob(jobId, new DefaultTimeZoneResolver(), DateTime.UtcNow); + + if (recurringJob != null) + { + _jobId = recurringJob.LastJobId; + } + jobData = connection.GetJobData(_jobId); + stateData = connection.GetStateData(_jobId); + } + + + + #line default + #line hidden +WriteLiteral(" \r\n"); + + + + #line 110 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + if (!IsReadOnly) + { + + + #line default + #line hidden +WriteLiteral(" \r\n"); + + + + #line 115 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + } + + + #line default + #line hidden +WriteLiteral(" \r\n"); + + + + #line 119 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + if (jobData == null) + { + + + #line default + #line hidden +WriteLiteral(" \r\n"); + + + + #line 122 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + } + else + { + + + #line default + #line hidden +WriteLiteral(" \r\n"); + + + +WriteLiteral(" \r\n"); + + + +WriteLiteral(" \r\n"); + + + +WriteLiteral(" \r\n"); + + + +WriteLiteral(" \r\n"); + + + + #line 143 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + } + + + #line default + #line hidden +WriteLiteral(" \r\n"); + + + + #line 145 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + } + + + #line default + #line hidden +WriteLiteral(" \r\n
\r\n " + +" \r\n "); + + + + #line 89 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + Write(Strings.Common_Id); + + + #line default + #line hidden +WriteLiteral(""); + + + + #line 90 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + Write(Strings.Common_State); + + + #line default + #line hidden +WriteLiteral(""); + + + + #line 91 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + Write(Strings.Common_Job); + + + #line default + #line hidden +WriteLiteral(""); + + + + #line 92 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + Write(Strings.Common_Reason); + + + #line default + #line hidden +WriteLiteral(""); + + + + #line 93 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + Write(Strings.Common_Retry); + + + #line default + #line hidden +WriteLiteral(""); + + + + #line 94 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + Write(Strings.Common_Created); + + + #line default + #line hidden +WriteLiteral("
\r\n " + +"
\r\n\r\n "); + + + + #line 150 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + Write(Html.Paginator(pager)); + + + #line default + #line hidden +WriteLiteral("\r\n
\r\n"); + + + + #line 152 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" + } + + + #line default + #line hidden +WriteLiteral("
\r\n
\r\n"); + + + + #line 155 "..\..\Dashboard\Pages\SuspendedJobsPage.cshtml" +} + + #line default + #line hidden + + } + } +} +#pragma warning restore 1591 diff --git a/src/Hangfire.Core/Hangfire.Core.csproj b/src/Hangfire.Core/Hangfire.Core.csproj index 1bcb0b1d8..cf85ba8d0 100644 --- a/src/Hangfire.Core/Hangfire.Core.csproj +++ b/src/Hangfire.Core/Hangfire.Core.csproj @@ -1,6 +1,6 @@  - net45;net46;netstandard1.3;netstandard2.0; + net48;net46;net45;netstandard1.3;netstandard2.0; portable false true @@ -8,7 +8,7 @@ Hangfire - + full $(DefineConstants);FEATURE_CRONDESCRIPTOR;FEATURE_OWIN; @@ -25,11 +25,11 @@
- + - + - + @@ -202,5 +202,8 @@ SucceededJobs.cshtml + + SuspendedJobsPage.cshtml +
\ No newline at end of file diff --git a/src/Hangfire.Core/IRecurringJobManager.cs b/src/Hangfire.Core/IRecurringJobManager.cs index 2b9b84485..e54d44c46 100644 --- a/src/Hangfire.Core/IRecurringJobManager.cs +++ b/src/Hangfire.Core/IRecurringJobManager.cs @@ -29,5 +29,17 @@ void AddOrUpdate( void Trigger([NotNull] string recurringJobId); void RemoveIfExists([NotNull] string recurringJobId); + + /// + /// Suspends the job from being processed. To resume call the ResumeJob method. + /// + /// + void SuspendJob(string jobId); + + /// + /// Resumes job processing for suspended jobs + /// + /// + void ResumeJob(string jobId); } } \ No newline at end of file diff --git a/src/Hangfire.Core/RecurringJobManager.cs b/src/Hangfire.Core/RecurringJobManager.cs index be425baf8..2f7fee3db 100644 --- a/src/Hangfire.Core/RecurringJobManager.cs +++ b/src/Hangfire.Core/RecurringJobManager.cs @@ -15,11 +15,12 @@ // License along with Hangfire. If not, see . using System; +using System.Linq; using Hangfire.Annotations; using Hangfire.Client; using Hangfire.Common; using Hangfire.Logging; -using Hangfire.Profiling; +using Hangfire.Profiling;// namespace Hangfire { @@ -54,7 +55,7 @@ public RecurringJobManager([NotNull] JobStorage storage, [NotNull] IJobFilterPro } public RecurringJobManager( - [NotNull] JobStorage storage, + [NotNull] JobStorage storage, [NotNull] IJobFilterProvider filterProvider, [NotNull] ITimeZoneResolver timeZoneResolver) : this(storage, filterProvider, timeZoneResolver, () => DateTime.UtcNow) @@ -62,8 +63,8 @@ public RecurringJobManager( } public RecurringJobManager( - [NotNull] JobStorage storage, - [NotNull] IJobFilterProvider filterProvider, + [NotNull] JobStorage storage, + [NotNull] IJobFilterProvider filterProvider, [NotNull] ITimeZoneResolver timeZoneResolver, [NotNull] Func nowFactory) : this(storage, new BackgroundJobFactory(filterProvider), timeZoneResolver, nowFactory) @@ -81,7 +82,7 @@ public RecurringJobManager([NotNull] JobStorage storage, [NotNull] IBackgroundJo } internal RecurringJobManager( - [NotNull] JobStorage storage, + [NotNull] JobStorage storage, [NotNull] IBackgroundJobFactory factory, [NotNull] ITimeZoneResolver timeZoneResolver, [NotNull] Func nowFactory) @@ -121,7 +122,7 @@ public void AddOrUpdate(string recurringJobId, Job job, string cronExpression, R } } } - + private static void ValidateCronExpression(string cronExpression) { try @@ -193,5 +194,56 @@ public void RemoveIfExists(string recurringJobId) transaction.Commit(); } } + + public void SuspendJob(string recurringJobId) + { + if (recurringJobId == null) throw new ArgumentNullException(nameof(recurringJobId)); + + using (var connection = _storage.GetConnection()) + using (connection.AcquireDistributedRecurringJobLock(recurringJobId, DefaultTimeout)) + { + var now = _nowFactory(); + var recurringJob = connection.GetRecurringJob(recurringJobId, _timeZoneResolver, now); + using (var transaction = connection.CreateWriteTransaction()) + { + transaction.AddToSet("paused-jobs", recurringJobId, JobHelper.ToTimestamp(DateTime.UtcNow)); + transaction.RemoveFromSet("recurring-jobs", recurringJobId); + transaction.Commit(); + } + } + } + + public void ResumeJob(string recurringJobId) + { + if (recurringJobId == null) + throw new ArgumentNullException(nameof(recurringJobId)); + + var monitor = _storage.GetMonitoringApi(); + var job = monitor.JobDetails(recurringJobId); + string jd = ""; + if (job != null && job.Job != null) + { + foreach (var RecurringJobId in job.Properties.Where(x => x.Key == "RecurringJobId")) + { + jd = RecurringJobId.Value; + } + } + + if (!string.IsNullOrEmpty(jd)) + recurringJobId = jd.Replace("\"", ""); + + using (var connection = _storage.GetConnection()) + { + using (connection.AcquireDistributedRecurringJobLock(recurringJobId, DefaultTimeout)) + { + using (var transaction = connection.CreateWriteTransaction()) + { + transaction.RemoveFromSet("paused-jobs", recurringJobId); + transaction.AddToSet("recurring-jobs", recurringJobId, JobHelper.ToTimestamp(DateTime.UtcNow)); + transaction.Commit(); + } + } + } + } } } diff --git a/src/Hangfire.SqlServer.Msmq/Hangfire.SqlServer.Msmq.csproj b/src/Hangfire.SqlServer.Msmq/Hangfire.SqlServer.Msmq.csproj index 83304bbe4..549a80ef1 100644 --- a/src/Hangfire.SqlServer.Msmq/Hangfire.SqlServer.Msmq.csproj +++ b/src/Hangfire.SqlServer.Msmq/Hangfire.SqlServer.Msmq.csproj @@ -1,6 +1,6 @@  - net45 + net48;net45 portable false true @@ -8,7 +8,7 @@ Hangfire.SqlServer.Msmq - + full diff --git a/src/Hangfire.SqlServer/Hangfire.SqlServer.csproj b/src/Hangfire.SqlServer/Hangfire.SqlServer.csproj index 5376afe51..5636109cd 100644 --- a/src/Hangfire.SqlServer/Hangfire.SqlServer.csproj +++ b/src/Hangfire.SqlServer/Hangfire.SqlServer.csproj @@ -1,6 +1,6 @@  - net45;netstandard1.3;netstandard2.0; + net48;net45;netstandard1.3;netstandard2.0; portable false true @@ -8,7 +8,7 @@ Hangfire.SqlServer - + full $(DefineConstants);FEATURE_TRANSACTIONSCOPE;FEATURE_CONFIGURATIONMANAGER @@ -30,11 +30,11 @@ - + - + diff --git a/tests/Hangfire.Core.Tests/Mocks/StateChangeContextMock.cs b/tests/Hangfire.Core.Tests/Mocks/StateChangeContextMock.cs index 53ab057e7..5afe70743 100644 --- a/tests/Hangfire.Core.Tests/Mocks/StateChangeContextMock.cs +++ b/tests/Hangfire.Core.Tests/Mocks/StateChangeContextMock.cs @@ -29,7 +29,6 @@ public StateChangeContextMock() BackgroundJobId, NewState.Object, ExpectedStates, - DisableFilters, CancellationToken, EmptyProfiler.Instance, CustomData));