From a5a8d18ddfb4fad9f03baa8a3edf5ff7190b18f6 Mon Sep 17 00:00:00 2001 From: Bryan Thornbury Date: Wed, 26 Jun 2019 10:52:35 -0700 Subject: [PATCH 1/3] Use TaskKey object to simplify api cognitive load --- Gofer.NET.Tests/GivenARecurringTask.cs | 7 ++-- Gofer.NET.Tests/GivenATaskScheduler.cs | 2 +- RecurringTask.cs | 8 ++-- ScheduledTask.cs | 8 ++-- TaskKey.cs | 53 +++++++++++++++++++++++++ TaskScheduler.cs | 54 +++++++++++++++----------- 6 files changed, 98 insertions(+), 34 deletions(-) create mode 100644 TaskKey.cs diff --git a/Gofer.NET.Tests/GivenARecurringTask.cs b/Gofer.NET.Tests/GivenARecurringTask.cs index 70aeaa2..2a9116a 100644 --- a/Gofer.NET.Tests/GivenARecurringTask.cs +++ b/Gofer.NET.Tests/GivenARecurringTask.cs @@ -15,7 +15,8 @@ public class GivenARecurringTask [Fact] public void ItPersistsPropertiesWhenSerializedAndDeserialized() { - var taskKey = $"{nameof(ItPersistsPropertiesWhenSerializedAndDeserialized)}::{Guid.NewGuid().ToString()}"; + var taskKey = + new TaskKey($"{nameof(ItPersistsPropertiesWhenSerializedAndDeserialized)}::{Guid.NewGuid().ToString()}"); var testTask = GetTestTask(() => Console.WriteLine(nameof(ItPersistsPropertiesWhenSerializedAndDeserialized))); @@ -43,7 +44,7 @@ public void ItProperlyDeterminesEquivalence() var taskInfo1 = GetTestTask(() => TestMethod1("hello world")); var taskInfo2 = GetTestTask(() => TestMethod2()); - var taskKey = $"{nameof(ItPersistsPropertiesWhenSerializedAndDeserialized)}::{Guid.NewGuid().ToString()}"; + var taskKey = new TaskKey($"{nameof(ItPersistsPropertiesWhenSerializedAndDeserialized)}::{Guid.NewGuid().ToString()}"); new RecurringTask(taskInfo1, TimeSpan.FromMinutes(5), taskKey) .IsEquivalent(new RecurringTask(taskInfo1, TimeSpan.FromMinutes(5), taskKey)) @@ -71,7 +72,7 @@ public void ItProperlyDeterminesEquivalence() Action differentIdsThrow = () => new RecurringTask(taskInfo1, TimeSpan.FromMinutes(5), taskKey) - .IsEquivalent(new RecurringTask(taskInfo1, TimeSpan.FromMinutes(5), "anothertaskkey")); + .IsEquivalent(new RecurringTask(taskInfo1, TimeSpan.FromMinutes(5), new TaskKey("anothertaskkey"))); differentIdsThrow.ShouldThrow(); } diff --git a/Gofer.NET.Tests/GivenATaskScheduler.cs b/Gofer.NET.Tests/GivenATaskScheduler.cs index db556dc..a7bdaeb 100644 --- a/Gofer.NET.Tests/GivenATaskScheduler.cs +++ b/Gofer.NET.Tests/GivenATaskScheduler.cs @@ -244,7 +244,7 @@ public async Task ItDoesNotBlowUpWhenCancelingNonExistentTasks() var taskQueue = TaskQueueTestFixture.UniqueRedisTaskQueue(); var taskScheduler = new TaskScheduler(taskQueue); - (await taskScheduler.CancelTask("hello")).Should().BeFalse(); + (await taskScheduler.CancelTask(new TaskKey("hello"))).Should().BeFalse(); } [Fact] diff --git a/RecurringTask.cs b/RecurringTask.cs index 300ad4d..d25e92f 100644 --- a/RecurringTask.cs +++ b/RecurringTask.cs @@ -12,7 +12,7 @@ public class RecurringTask public string LockKey => $"{nameof(RecurringTask)}::{TaskKey}::ScheduleLock"; [JsonProperty] - public string TaskKey { get; private set; } + public TaskKey TaskKey { get; private set; } [JsonProperty] public DateTime StartTime { get; private set; } @@ -34,7 +34,7 @@ public RecurringTask() { } public RecurringTask( TaskInfo taskInfo, TimeSpan interval, - string taskKey) : this(taskInfo, taskKey) + TaskKey taskKey) : this(taskInfo, taskKey) { Interval = interval; @@ -44,7 +44,7 @@ public RecurringTask( public RecurringTask( TaskInfo taskInfo, string crontab, - string taskKey) : this(taskInfo, taskKey) + TaskKey taskKey) : this(taskInfo, taskKey) { ValidateCrontab(crontab); Crontab = crontab; @@ -52,7 +52,7 @@ public RecurringTask( FirstRunTime = GetNextRunTime(DateTime.UtcNow); } - private RecurringTask(TaskInfo taskInfo, string taskKey) + private RecurringTask(TaskInfo taskInfo, TaskKey taskKey) { TaskInfo = taskInfo; StartTime = DateTime.UtcNow; diff --git a/ScheduledTask.cs b/ScheduledTask.cs index 735a822..00298aa 100644 --- a/ScheduledTask.cs +++ b/ScheduledTask.cs @@ -12,7 +12,7 @@ public class ScheduledTask public string LockKey => $"{nameof(ScheduledTask)}::{TaskKey}::ScheduleLock"; [JsonProperty] - public string TaskKey { get; private set; } + public TaskKey TaskKey { get; private set; } [JsonProperty] public long ScheduledUnixTimeMilliseconds { get; private set; } @@ -25,7 +25,7 @@ public ScheduledTask() { } public ScheduledTask( TaskInfo taskInfo, TimeSpan offset, - string taskKey) : this(taskInfo, new DateTimeOffset(DateTime.UtcNow + offset), taskKey) + TaskKey taskKey) : this(taskInfo, new DateTimeOffset(DateTime.UtcNow + offset), taskKey) { } @@ -33,14 +33,14 @@ public ScheduledTask( TaskInfo taskInfo, DateTime scheduledTime, TaskQueue taskQueue, - string taskKey) : this(taskInfo, new DateTimeOffset(scheduledTime), taskKey) + TaskKey taskKey) : this(taskInfo, new DateTimeOffset(scheduledTime), taskKey) { } public ScheduledTask( TaskInfo taskInfo, DateTimeOffset scheduledDateTimeOffset, - string taskKey) + TaskKey taskKey) { TaskKey = taskKey; TaskInfo = taskInfo; diff --git a/TaskKey.cs b/TaskKey.cs new file mode 100644 index 0000000..5be4f74 --- /dev/null +++ b/TaskKey.cs @@ -0,0 +1,53 @@ +using System; + +namespace Gofer.NET +{ + public class TaskKey + { + public string Value { get; set; } + + public string RecurringTaskName { get; set; } + + public static TaskKey CreateUnique() + { + return new TaskKey($"{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}::{Guid.NewGuid().ToString()}"); + } + + public static TaskKey CreateRecurring(string recurringTaskName) + { + return new TaskKey($"Recurring{nameof(TaskKey)}::{recurringTaskName}", recurringTaskName); + } + + public TaskKey(string value) + { + Value = value; + RecurringTaskName = null; + } + + public TaskKey(string value, string recurringTaskName) + { + Value = value; + RecurringTaskName = recurringTaskName; + } + + public override bool Equals(object obj) + { + if (obj == null || GetType() != obj.GetType()) + { + return false; + } + + return string.Equals(Value, ((TaskKey) obj).Value); + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + + public override string ToString() + { + return Value; + } + } +} \ No newline at end of file diff --git a/TaskScheduler.cs b/TaskScheduler.cs index babb045..b1481f1 100644 --- a/TaskScheduler.cs +++ b/TaskScheduler.cs @@ -69,7 +69,7 @@ public async Task Tick(bool forceRunPromotion=false) public async Task AddScheduledTask(Expression action, TimeSpan offsetFromNow) { - var scheduledTask = new ScheduledTask(action.ToTaskInfo(), offsetFromNow, UniqueTaskKey()); + var scheduledTask = new ScheduledTask(action.ToTaskInfo(), offsetFromNow, TaskKey.CreateUnique()); await EnqueueScheduledTask(scheduledTask); @@ -78,7 +78,7 @@ public async Task AddScheduledTask(Expression action, Tim public async Task AddScheduledTask(Expression action, DateTimeOffset scheduledTime) { - var scheduledTask = new ScheduledTask(action.ToTaskInfo(), scheduledTime, UniqueTaskKey()); + var scheduledTask = new ScheduledTask(action.ToTaskInfo(), scheduledTime, TaskKey.CreateUnique()); await EnqueueScheduledTask(scheduledTask); @@ -87,7 +87,7 @@ public async Task AddScheduledTask(Expression action, Dat public async Task AddScheduledTask(Expression action, DateTime scheduledTime) { - var scheduledTask = new ScheduledTask(action.ToTaskInfo(), scheduledTime, UniqueTaskKey()); + var scheduledTask = new ScheduledTask(action.ToTaskInfo(), scheduledTime, TaskKey.CreateUnique()); await EnqueueScheduledTask(scheduledTask); @@ -96,7 +96,8 @@ public async Task AddScheduledTask(Expression action, Dat public async Task AddRecurringTask(Expression action, TimeSpan interval, string taskName) { - var recurringTask = new RecurringTask(action.ToTaskInfo(), interval, RecurringTaskKey(taskName)); + var recurringTask = new RecurringTask(action.ToTaskInfo(), interval, + TaskKey.CreateRecurring(taskName)); if (await RecurringTaskDoesNotExistOrNeedsChange(recurringTask)) { @@ -109,7 +110,8 @@ public async Task AddRecurringTask(Expression action, Tim public async Task AddRecurringTask(Expression action, string crontab, string taskName) { - var recurringTask = new RecurringTask(action.ToTaskInfo(), crontab, RecurringTaskKey(taskName)); + var recurringTask = new RecurringTask(action.ToTaskInfo(), crontab, + TaskKey.CreateRecurring(taskName)); if (await RecurringTaskDoesNotExistOrNeedsChange(recurringTask)) { @@ -124,12 +126,17 @@ public async Task CancelRecurringTask(RecurringTask recurringTask) return await CancelTask(recurringTask.TaskKey); } + public async Task CancelRecurringTask(string taskName) + { + return await CancelTask(TaskKey.CreateRecurring(taskName)); + } + public async Task CancelScheduledTask(ScheduledTask scheduledTask) { return await CancelTask(scheduledTask.TaskKey); } - public async Task CancelTask(string taskKey) + public async Task CancelTask(TaskKey taskKey) { if (LoadedCancelTaskScript == null) { @@ -142,12 +149,25 @@ public async Task CancelTask(string taskKey) (RedisKey) ScheduledTasksMapKey, }, new [] { - (RedisValue) taskKey + (RedisValue) taskKey.Value }); return (bool) didCancel; } + public async Task GetRecurringTask(TaskKey taskKey) + { + var serializedRecurringTask = await _taskQueue.Backend.GetMapField(ScheduledTasksMapKey, + $"serializedRecurringTask::{taskKey.Value}"); + + if (string.IsNullOrEmpty(serializedRecurringTask)) + return null; + + var recurringTask = JsonTaskInfoSerializer.Deserialize(serializedRecurringTask); + + return recurringTask; + } + public async Task GetRecurringTask(string taskKey) { var serializedRecurringTask = await _taskQueue.Backend.GetMapField(ScheduledTasksMapKey, @@ -198,7 +218,7 @@ private async Task EnqueueRecurringTask(RecurringTask recurringTask) { var serializedTaskInfo = JsonTaskInfoSerializer.Serialize(recurringTask.TaskInfo); await _taskQueue.Backend.SetMapFields(ScheduledTasksMapKey, - (recurringTask.TaskKey, serializedTaskInfo), + (recurringTask.TaskKey.Value, serializedTaskInfo), ($"isRecurring::{recurringTask.TaskKey}", true), ($"serializedRecurringTask::{recurringTask.TaskKey}", JsonTaskInfoSerializer.Serialize(recurringTask))); @@ -207,7 +227,7 @@ await _taskQueue.Backend.SetMapFields(ScheduledTasksMapKey, await _taskQueue.Backend.AddToOrderedSet( ScheduledTasksOrderedSetKey, nextRunTimestamp, - recurringTask.TaskKey); + recurringTask.TaskKey.Value); } private async Task EnqueueScheduledTask(ScheduledTask scheduledTask) @@ -215,13 +235,13 @@ private async Task EnqueueScheduledTask(ScheduledTask scheduledTask) var serializedTaskInfo = JsonTaskInfoSerializer.Serialize(scheduledTask.TaskInfo); await _taskQueue.Backend.SetMapFields(ScheduledTasksMapKey, - (scheduledTask.TaskKey, serializedTaskInfo), + (scheduledTask.TaskKey.Value, serializedTaskInfo), ($"isRecurring::{scheduledTask.TaskKey}", false)); await _taskQueue.Backend.AddToOrderedSet( ScheduledTasksOrderedSetKey, scheduledTask.ScheduledUnixTimeMilliseconds, - scheduledTask.TaskKey); + scheduledTask.TaskKey.Value); } private async Task RescheduleRecurringTasks() @@ -251,7 +271,7 @@ private async Task RescheduleRecurringTasks() recurringTask = JsonTaskInfoSerializer.Deserialize(serializedRecurringTask); nextRunTimestamp = recurringTask.GetNextRunTimestamp(DateTime.UtcNow); - args.Add((RedisValue) recurringTask.TaskKey); + args.Add((RedisValue) recurringTask.TaskKey.Value); args.Add((RedisValue) nextRunTimestamp); } @@ -362,15 +382,5 @@ private string LuaScriptToCancelTask() return false "; } - - private string UniqueTaskKey() - { - return $"{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}::{Guid.NewGuid().ToString()}"; - } - - private string RecurringTaskKey(string taskName) - { - return $"{nameof(RecurringTaskKey)}::{taskName}"; - } } } From bc278180017d6c6566b31f055950c2ad18b6bba5 Mon Sep 17 00:00:00 2001 From: Bryan Thornbury Date: Wed, 26 Jun 2019 11:15:12 -0700 Subject: [PATCH 2/3] Add TaskKey json constructor --- TaskKey.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TaskKey.cs b/TaskKey.cs index 5be4f74..edb23c7 100644 --- a/TaskKey.cs +++ b/TaskKey.cs @@ -18,6 +18,8 @@ public static TaskKey CreateRecurring(string recurringTaskName) return new TaskKey($"Recurring{nameof(TaskKey)}::{recurringTaskName}", recurringTaskName); } + public TaskKey() {} + public TaskKey(string value) { Value = value; From cd2aed29ed5898d466dfd8992267719c0e650430 Mon Sep 17 00:00:00 2001 From: Bryan Thornbury Date: Wed, 26 Jun 2019 12:55:17 -0700 Subject: [PATCH 3/3] Use .Equals for object comparison --- RecurringTask.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RecurringTask.cs b/RecurringTask.cs index d25e92f..2112859 100644 --- a/RecurringTask.cs +++ b/RecurringTask.cs @@ -122,7 +122,7 @@ public bool IsEquivalent(RecurringTask comparisonRecurringTask) throw new ArgumentException("comparisonRecurringTask must not be null"); } - if (comparisonRecurringTask.TaskKey != TaskKey) + if (!comparisonRecurringTask.TaskKey.Equals(TaskKey)) { throw new Exception("Cannot compare with a recurringTask with a different TaskKey."); }