PsQueue.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. using System.Collections.Concurrent;
  2. using qdr.fnd.core.pqueue.Configuration;
  3. using qdr.fnd.core.pqueue.Enums;
  4. using qdr.fnd.core.pqueue.Exceptions;
  5. using qdr.fnd.core.pqueue.Process;
  6. using qdr.fnd.core.pqueue.Storages;
  7. using Quadarax.Foundation.Core.Data;
  8. using Quadarax.Foundation.Core.Logging;
  9. using Quadarax.Foundation.Core.Thread;
  10. using Quadarax.Foundation.Core.Value.Generators;
  11. namespace qdr.fnd.core.pqueue
  12. {
  13. public class PsQueue : LoopWorker
  14. {
  15. #region *** Constants ***
  16. private const string CS_DEF_QUEUE_NAME = "PSQUEUE";
  17. #endregion
  18. #region *** Properties ***
  19. public PsQueueConfiguration Configuration { get;}
  20. public event PsQueueStatusChangedEventHandler? ProcessItemStatusChanged;
  21. #endregion
  22. #region *** Private Fields ***
  23. private TinyHash _hashGenerator;
  24. protected IPsQueueStorage _storage;
  25. private List<Tuple<PsStatusEnum,PsStatusEnum[]>> _statusTransitions = new List<Tuple<PsStatusEnum, PsStatusEnum[]>>();
  26. private ConcurrentDictionary<string, IPsProcessProvider> _workingItems = new ConcurrentDictionary<string, IPsProcessProvider>();
  27. #endregion
  28. #region *** Constructors ***
  29. public PsQueue(string queueName, PsQueueConfiguration configuration,IPsQueueStorage storageProvider, ILogger logger) : base(queueName,null)
  30. {
  31. _hashGenerator = new TinyHash(5);
  32. if (string.IsNullOrEmpty(queueName))
  33. queueName =CS_DEF_QUEUE_NAME + _hashGenerator.NewId();
  34. Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
  35. _storage = storageProvider ?? throw new ArgumentNullException(nameof(storageProvider));
  36. _statusTransitions.Add(new Tuple<PsStatusEnum, PsStatusEnum[]>(PsStatusEnum.New,new[]
  37. {
  38. PsStatusEnum.Pending
  39. }));
  40. _statusTransitions.Add(new Tuple<PsStatusEnum, PsStatusEnum[]>(PsStatusEnum.Pending,new[]
  41. {
  42. PsStatusEnum.Started,
  43. PsStatusEnum.Expired
  44. }));
  45. _statusTransitions.Add(new Tuple<PsStatusEnum, PsStatusEnum[]>(PsStatusEnum.Started,new[]
  46. {
  47. PsStatusEnum.Pending,
  48. PsStatusEnum.Paused,
  49. PsStatusEnum.Stopped,
  50. PsStatusEnum.DoneOk,
  51. PsStatusEnum.DoneFailed,
  52. PsStatusEnum.Expired
  53. }));
  54. _statusTransitions.Add(new Tuple<PsStatusEnum, PsStatusEnum[]>(PsStatusEnum.Paused,new[]
  55. {
  56. PsStatusEnum.Started,
  57. PsStatusEnum.Stopped,
  58. PsStatusEnum.Expired
  59. }));
  60. _statusTransitions.Add(new Tuple<PsStatusEnum, PsStatusEnum[]>(PsStatusEnum.Stopped,new[]
  61. {
  62. PsStatusEnum.DoneFailed,
  63. PsStatusEnum.Expired
  64. }));
  65. _statusTransitions.Add(new Tuple<PsStatusEnum, PsStatusEnum[]>(PsStatusEnum.DoneOk,new[]
  66. {
  67. PsStatusEnum.Deleted
  68. }));
  69. _statusTransitions.Add(new Tuple<PsStatusEnum, PsStatusEnum[]>(PsStatusEnum.DoneFailed,new[]
  70. {
  71. PsStatusEnum.Deleted
  72. }));
  73. _statusTransitions.Add(new Tuple<PsStatusEnum, PsStatusEnum[]>(PsStatusEnum.Expired,new[]
  74. {
  75. PsStatusEnum.Deleted
  76. }));
  77. _statusTransitions.Add(new Tuple<PsStatusEnum, PsStatusEnum[]>(PsStatusEnum.Deleted,Array.Empty<PsStatusEnum>()));
  78. }
  79. #endregion
  80. #region *** Public Operations ***
  81. public string EnQueue(IPsQueueItemDescriptor item)
  82. {
  83. if (item is null)
  84. throw new ArgumentNullException(nameof(item));
  85. ValidateItem(item);
  86. var itemId = _storage.CreateItem(item.Owner, item.OwnerRef, item.ProcessProviderClass, item.AtteptsInitial, null);
  87. foreach (var argument in item.Arguments)
  88. {
  89. if (argument.ValueType.FullName == null) throw new InvalidOperationException($"Cannot obtain fullName of type '{argument.ValueType.Name}'");
  90. _storage.CreateItemAttribute(itemId, argument.Name, argument.Value, argument.ValueType.FullName, argument.IsOutput);
  91. }
  92. Log(LogSeverityEnum.Info,$"{Name} - Item [{itemId}] was enqueued as {item}");
  93. return itemId;
  94. }
  95. public bool Dequeue(string itemId, bool force)
  96. {
  97. throw new NotImplementedException();
  98. }
  99. public IQueryable<IPsQueueItem> Query(string? owner, string? ownerRef, params Tuple<bool,PsStatusEnum>[] status)
  100. {
  101. throw new NotImplementedException();
  102. }
  103. public IPsQueueItem? Get(string itemId)
  104. {
  105. throw new NotImplementedException();
  106. }
  107. public bool Postpone(string itemId, TimeSpan postpone)
  108. {
  109. throw new NotImplementedException();
  110. }
  111. public bool Reset(string itemId)
  112. {
  113. throw new NotImplementedException();
  114. }
  115. public bool SetPriority(string itemId, int priority)
  116. {
  117. throw new NotImplementedException();
  118. }
  119. public IEnumerable<IPsQueueItem> GetWorkingItems()
  120. {
  121. throw new NotImplementedException();
  122. }
  123. #endregion
  124. #region *** Private Operations ***
  125. #region Overrides of LoopWorker
  126. /// <inheritdoc />
  127. protected override Task DoIteration(IWorkerContext? context)
  128. {
  129. var workersLeft = Configuration.MaxConcurrentProcesses - _workingItems.Count;
  130. if (workersLeft>0)
  131. {
  132. var itemsToProcess = Query(null, null,
  133. new Tuple<bool, PsStatusEnum>[] { new(false, PsStatusEnum.Pending) })
  134. .Where(x => x.ValidFrom > DateTime.Now && (x.ValidTo == null || x.ValidTo < DateTime.Now)).Take(workersLeft);
  135. foreach(var item in itemsToProcess){
  136. // set to Pending Transient to Started
  137. _storage.UpdateItemStatus(item.Id, null, true);
  138. }
  139. }
  140. return base.DoIteration(context);
  141. }
  142. #endregion
  143. private void ValidateItem(IPsQueueItemDescriptor item)
  144. {
  145. if (item is null)
  146. throw new ArgumentNullException(nameof(item));
  147. if (string.IsNullOrEmpty(item.Owner))
  148. throw new PSQueueValidationException(PSQueueValidationException.ErrorCodes.OwnerRequired);
  149. if (string.IsNullOrEmpty(item.OwnerRef))
  150. throw new PSQueueValidationException(PSQueueValidationException.ErrorCodes.OwnerRefRequired);
  151. var provider = ResolveProvider(item.ProcessProviderClass, item.Arguments);
  152. provider.Validate();
  153. if (item.AtteptsInitial <= 0);
  154. throw new PSQueueValidationException(PSQueueValidationException.ErrorCodes.InitialAttemptMustBePositive);
  155. }
  156. private void SetItemStatus(IPsQueueItem item, PsStatusEnum? newStatus, bool isTransient, string message)
  157. {
  158. var oldStatus = item.Status;
  159. var oldIsTransient = item.IsStatusTransient;
  160. if (!isTransient && newStatus == null)
  161. throw new PSQueueValidationException(PSQueueValidationException.ErrorCodes.InvalidStatusTransientCombination);
  162. }
  163. private IPsProcessProvider ResolveProvider(string providerClass, IEnumerable<ITypedArgument> arguments)
  164. {
  165. if (string.IsNullOrEmpty(providerClass))
  166. throw new PSQueueValidationException(PSQueueValidationException.ErrorCodes.ProcessProviderRequired);
  167. var providerType = Type.GetType(providerClass);
  168. if (providerType == null)
  169. throw new PSQueueValidationException(PSQueueValidationException.ErrorCodes.ProcessProviderCannotResolve, providerClass);
  170. var provider = Activator.CreateInstance(providerType, arguments) as IPsProcessProvider;
  171. if (provider == null)
  172. throw new PSQueueValidationException(PSQueueValidationException.ErrorCodes.ProcessProviderCannotResolve, providerClass);
  173. return provider;
  174. }
  175. protected override void OnDisposing()
  176. {
  177. base.OnDisposing();
  178. ProcessItemStatusChanged = null;
  179. _storage.Dispose();
  180. //TODO: Implement OnDisposing
  181. }
  182. #endregion
  183. }
  184. }