/// <inheritdoc />
        public void Complete(Guid taskId, DateTime timestampUtc)
        {
            Trace.WriteLine("ENTER: Record task '{0}' completed ...".FormatInvariant(taskId));

            string taskIdAsString = RedisConverter.ToString(taskId);

            string entityKey = RedisTaskRuntimeInfoRepository.GetEntityKey(taskIdAsString);

            RedisTaskRuntimeInfo taskInfo = this.GetById(taskId, false);

            taskInfo.Percentage   = 100;
            taskInfo.Status       = TaskStatus.Success;
            taskInfo.CompletedUtc = timestampUtc;

            byte[] content = this.serializer.Serialize(taskInfo);

            using (IRedisTransaction transaction = this.Provider.CreateTransaction())
            {
                transaction.RemoveKey(entityKey);
                transaction.RemoveFromList(RedisTaskRuntimeInfoRepository.ActiveTasksList, taskIdAsString);
                transaction.SetHashValue(RedisTaskRuntimeInfoRepository.ArchiveTasksHash, taskIdAsString, content);

                transaction.Commit();
            }

            Trace.WriteLine("EXIT: Task '{0}' completed recorded.".FormatInvariant(taskId));
        }
        private RedisTaskRuntimeInfo GetById(Guid taskId, bool fromArchive)
        {
            string taskIdAsString = RedisConverter.ToString(taskId);

            string entityKey = RedisTaskRuntimeInfoRepository.GetEntityKey(taskIdAsString);

            if (fromArchive)
            {
                byte[] content = this.provider.GetHashBinaryValue(RedisTaskRuntimeInfoRepository.ArchiveTasksHash, taskIdAsString);

                if (content == null)
                {
                    return(null);
                }

                return((RedisTaskRuntimeInfo)this.serializer.Deserialize(content, typeof(RedisTaskRuntimeInfo)));
            }
            else
            {
                IReadOnlyDictionary <string, string> values = this.provider.GetHashAsText(entityKey);

                if (values.Count == 0)
                {
                    return(null);
                }

                return(RedisTaskRuntimeInfoRepository.Convert(values));
            }
        }
        /// <inheritdoc />
        public void Fail(Guid taskId, DateTime timestampUtc, Exception error)
        {
            if (error == null)
            {
                throw new ArgumentNullException(nameof(error));
            }

            Trace.WriteLine("ENTER: Record task '{0}' failed with error '{1}' ...".FormatInvariant(taskId, error.Message));

            string taskIdAsString = RedisConverter.ToString(taskId);

            string entityKey = RedisTaskRuntimeInfoRepository.GetEntityKey(taskIdAsString);

            RedisTaskRuntimeInfo taskInfo = this.GetById(taskId, false);

            taskInfo.Status       = TaskStatus.Failed;
            taskInfo.CompletedUtc = timestampUtc;
            taskInfo.Error        = error.ToString();

            byte[] content = this.serializer.Serialize(taskInfo);

            using (IRedisTransaction transaction = this.Provider.CreateTransaction())
            {
                transaction.RemoveKey(entityKey);
                transaction.RemoveFromList(RedisTaskRuntimeInfoRepository.ActiveTasksList, taskIdAsString);
                transaction.AddToList(RedisTaskRuntimeInfoRepository.FailedTasksList, taskIdAsString);
                transaction.SetHashValue(RedisTaskRuntimeInfoRepository.ArchiveTasksHash, taskIdAsString, content);

                transaction.Commit();
            }

            Trace.WriteLine("EXIT: Task '{0}' failed with error '{1}' recorded.".FormatInvariant(taskId, error.Message));
        }
        /// <inheritdoc />
        public void Assign(Guid taskId, Guid?taskProcessorId)
        {
            Trace.WriteLine("ENTER: Recording task '{0}' assigned to processor '{1}' ...".FormatInvariant(taskId, taskProcessorId));

            string entityKey = RedisTaskRuntimeInfoRepository.GetEntityKey(taskId);

            this.Provider.SetHashValue(entityKey, "TaskProcessorId", RedisConverter.ToString(taskProcessorId));

            Trace.WriteLine("EXIT: Task '{0}' assigned to processor '{1}' recorded.".FormatInvariant(taskId, taskProcessorId));
        }
        /// <inheritdoc />
        public bool CheckIsPendingOrActive(Guid taskId)
        {
            Trace.WriteLine("ENTER: Checking if task '{0}' is pending or active ...".FormatInvariant(taskId));

            string entityKey = RedisTaskRuntimeInfoRepository.GetEntityKey(taskId);

            bool result = this.provider.ContainsKey(entityKey);

            Trace.WriteLine("EXIT: Check whether task '{0}' is pending or active completed. Result is {1}.".FormatInvariant(taskId, result));

            return(result);
        }
        /// <inheritdoc />
        public void Progress(Guid taskId, double percentage)
        {
            Trace.WriteLine("ENTER: Record task '{0}' progress to {1}% ...".FormatInvariant(taskId, percentage));

            if ((percentage < 0) || (percentage > 100))
            {
                throw new ArgumentOutOfRangeException("percentage");
            }

            string entityKey = RedisTaskRuntimeInfoRepository.GetEntityKey(taskId);

            this.Provider.SetHashValue(entityKey, "Percentage", RedisConverter.ToString(percentage));

            Trace.WriteLine("ENTER: Task '{0}' progress to {1}% recorded.".FormatInvariant(taskId, percentage));
        }
        private Dictionary <TaskStatus, IEnumerable <ITaskRuntimeInfo> > GetAllByType(params string[] listKeys)
        {
            List <string> entityIds = new List <string>();

            using (IRedisPipeline pipeline = this.provider.CreatePipeline())
            {
                foreach (string listKey in listKeys)
                {
                    pipeline.GetList(listKey, values => entityIds.AddRange(values));
                }

                pipeline.Flush();
            }

            Dictionary <TaskStatus, IEnumerable <ITaskRuntimeInfo> > result = new Dictionary <TaskStatus, IEnumerable <ITaskRuntimeInfo> >();

            using (IRedisPipeline pipeline = this.provider.CreatePipeline())
            {
                foreach (string entityId in entityIds)
                {
                    string entityKey = RedisTaskRuntimeInfoRepository.GetEntityKey(entityId);

                    pipeline.GetHash(entityKey, values =>
                    {
                        if (values.Count > 0)
                        {
                            ITaskRuntimeInfo taskInfo = RedisTaskRuntimeInfoRepository.Convert(values);

                            IEnumerable <ITaskRuntimeInfo> collection;

                            if (!result.TryGetValue(taskInfo.Status, out collection))
                            {
                                collection = new List <ITaskRuntimeInfo>();

                                result.Add(taskInfo.Status, collection);
                            }

                            ((ICollection <ITaskRuntimeInfo>)collection).Add(taskInfo);
                        }
                    });
                }

                pipeline.Flush();
            }

            return(result);
        }
        /// <inheritdoc />
        public void Add(ITaskRuntimeInfo taskInfo)
        {
            if (taskInfo == null)
            {
                throw new ArgumentNullException("taskInfo");
            }

            Trace.WriteLine("ENTER: Adding runtime information for task '{0}' of type '{1}' with priority '{2}' in polling queue '{3}' ...".FormatInvariant(taskInfo.TaskId, taskInfo.TaskType, taskInfo.Priority, taskInfo.PollingQueue));

            taskInfo.ValidateForAdd();

            string entityKey = RedisTaskRuntimeInfoRepository.GetEntityKey(taskInfo.TaskId);

            string addToListKey;

            Dictionary <string, string> values = new Dictionary <string, string>()
            {
                { "Id", RedisConverter.ToString(taskInfo.TaskId) },
                { "TaskType", RedisConverter.ToString(taskInfo.TaskType, true) },
                { "SubmittedUtc", RedisConverter.ToString(taskInfo.SubmittedUtc) },
                { "Status", RedisConverter.ToString(taskInfo.Status) }
            };

            if (string.IsNullOrEmpty(taskInfo.PollingQueue))
            {
                values.Add("Priority", RedisConverter.ToString(taskInfo.Priority));

                addToListKey = RedisTaskRuntimeInfoRepository.PendingTasksList;
            }
            else
            {
                values.Add("PollingQueue", taskInfo.PollingQueue);

                addToListKey = RedisTaskRuntimeInfoRepository.GetPollingQueueRedisKey(taskInfo.PollingQueue, TaskStatus.Pending);
            }

            using (IRedisTransaction transaction = this.Provider.CreateTransaction())
            {
                transaction.SetHashValues(entityKey, values);

                transaction.AddToList(addToListKey, RedisConverter.ToString(taskInfo.TaskId));

                transaction.Commit();
            }

            Trace.WriteLine("EXIT: Runtime information for task '{0}' of type '{1}' with priority '{2}' in polling queue '{3}' added.".FormatInvariant(taskInfo.TaskId, taskInfo.TaskType, taskInfo.Priority, taskInfo.PollingQueue));
        }
        /// <inheritdoc />
        public Type GetTaskType(Guid taskId)
        {
            Trace.WriteLine("ENTER: Getting task '{0}' type ...".FormatInvariant(taskId));

            string entityKey = RedisTaskRuntimeInfoRepository.GetEntityKey(taskId);

            string taskTypeAsString = this.provider.GetHashTextValue(entityKey, "TaskType");

            Type result = null;

            if (!string.IsNullOrEmpty(taskTypeAsString))
            {
#if DEBUG
                result = Type.GetType(taskTypeAsString, true);
#else
                result = Type.GetType(taskTypeAsString, false);

                if (result == null)
                {
                    Trace.TraceWarning("EXIT: Task '{0}' type '{1}' cannot be resolved.", taskId, taskTypeAsString);

                    return(null);
                }
#endif
            }
            else
            {
                RedisTaskRuntimeInfo taskInfo = this.GetById(taskId, true);

                if (taskInfo != null)
                {
                    result = taskInfo.TaskType;
                }
            }

            if (result == null)
            {
                Trace.WriteLine("EXIT: Task '{0}' type was not found neither in hash nor in archive.".FormatInvariant(taskId));
            }
            else
            {
                Trace.WriteLine("EXIT: Return task '{0}' type '{1}'.".FormatInvariant(taskId, result));
            }

            return(result);
        }
        /// <inheritdoc />
        public IEnumerable <ITaskRuntimeInfo> ReservePollingQueueTasks(string pollingQueueKey, int maxResults)
        {
            Trace.WriteLine("ENTER: Reserving {0} polling queue '{1}' tasks for execution ...".FormatInvariant(maxResults, pollingQueueKey));

            if (string.IsNullOrEmpty(pollingQueueKey))
            {
                throw new ArgumentNullException(nameof(pollingQueueKey));
            }

            if (maxResults < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(maxResults), maxResults, "Value must not be negative.");
            }

            string pollingQueueListKey = RedisTaskRuntimeInfoRepository.GetPollingQueueRedisKey(pollingQueueKey, TaskStatus.Pending);

            List <string> entityIds = new List <string>();

            using (IRedisPipeline pipeline = this.provider.CreatePipeline())
            {
                for (int i = 0; i < maxResults; i++)
                {
                    pipeline.PopFirstListElementAsText(pollingQueueListKey, value =>
                    {
                        if (!string.IsNullOrEmpty(value))
                        {
                            entityIds.Add(value);
                        }
                    });
                }

                pipeline.Flush();
            }

            ICollection <ITaskRuntimeInfo> result = this.GetAll(entityIds);

            Trace.WriteLine("EXIT: {0} polling queue '{1}' tasks reserved for execution.".FormatInvariant(result.Count, pollingQueueKey));

            return(result);
        }
        private List <ITaskRuntimeInfo> GetAll(IEnumerable <string> entityIds)
        {
            List <ITaskRuntimeInfo> result = new List <ITaskRuntimeInfo>();

            using (IRedisPipeline pipeline = this.provider.CreatePipeline())
            {
                foreach (string entityId in entityIds)
                {
                    string entityKey = RedisTaskRuntimeInfoRepository.GetEntityKey(entityId);

                    pipeline.GetHash(entityKey, values =>
                    {
                        if (values.Count > 0)
                        {
                            result.Add(RedisTaskRuntimeInfoRepository.Convert(values));
                        }
                    });
                }

                pipeline.Flush();
            }

            return(result);
        }
        /// <inheritdoc />
        public void Start(Guid taskId, Guid taskProcessorId, DateTime timestampUtc)
        {
            Trace.WriteLine("ENTER: Recording task '{0}' started by processor '{1}' ...".FormatInvariant(taskId, taskProcessorId));

            string entityKey = RedisTaskRuntimeInfoRepository.GetEntityKey(taskId);

            Dictionary <string, string> values = new Dictionary <string, string>()
            {
                { "Status", RedisConverter.ToString(TaskStatus.InProgress) },
                { "TaskProcessorId", RedisConverter.ToString(taskProcessorId) },
                { "StartedUtc", RedisConverter.ToString(timestampUtc) }
            };

            using (IRedisTransaction transaction = this.Provider.CreateTransaction())
            {
                transaction.SetHashValues(entityKey, values);
                transaction.RemoveFromList(RedisTaskRuntimeInfoRepository.PendingTasksList, RedisConverter.ToString(taskId));
                transaction.AddToList(RedisTaskRuntimeInfoRepository.ActiveTasksList, RedisConverter.ToString(taskId));

                transaction.Commit();
            }

            Trace.WriteLine("EXIT: Task '{0}' assigned to processor '{1}' recorded.".FormatInvariant(taskId, taskProcessorId));
        }
 private static string GetEntityKey(Guid entityId)
 {
     return(RedisTaskRuntimeInfoRepository.GetEntityKey(RedisConverter.ToString(entityId)));
 }