PsQueue.cs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  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, IPsQueue
  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 IQueryable<IPsQueueItem> Query(string? owner, string? ownerRef, params Tuple<bool,PsStatusEnum>[] status)
  96. {
  97. throw new NotImplementedException();
  98. }
  99. public IPsQueueItem? Get(string itemId)
  100. {
  101. throw new NotImplementedException();
  102. }
  103. public bool Postpone(string itemId, TimeSpan postpone)
  104. {
  105. throw new NotImplementedException();
  106. }
  107. public bool Reset(string itemId)
  108. {
  109. throw new NotImplementedException();
  110. }
  111. public bool SetPriority(string itemId, int priority)
  112. {
  113. throw new NotImplementedException();
  114. }
  115. public IEnumerable<IPsQueueItem> GetWorkingItems()
  116. {
  117. throw new NotImplementedException();
  118. }
  119. #endregion
  120. #region *** Private Operations ***
  121. #region Overrides of LoopWorker
  122. /// <inheritdoc />
  123. protected override Task DoIteration(IWorkerContext? context)
  124. {
  125. var workersLeft = Configuration.MaxConcurrentProcesses - _workingItems.Count;
  126. if (workersLeft>0)
  127. {
  128. var itemsToProcess = Query(null, null,
  129. new Tuple<bool, PsStatusEnum>[] { new(false, PsStatusEnum.Pending) })
  130. .Where(x => x.ValidFrom > DateTime.Now && (x.ValidTo == null || x.ValidTo < DateTime.Now)).Take(workersLeft);
  131. foreach(var item in itemsToProcess){
  132. // set to Pending Transient to Started
  133. _storage.UpdateItemStatus(item.Id, null, true);
  134. }
  135. }
  136. return base.DoIteration(context);
  137. }
  138. #endregion
  139. private void ValidateItem(IPsQueueItemDescriptor item)
  140. {
  141. if (item is null)
  142. throw new ArgumentNullException(nameof(item));
  143. if (string.IsNullOrEmpty(item.Owner))
  144. throw new PSQueueValidationException(PSQueueValidationException.ErrorCodes.OwnerRequired);
  145. if (string.IsNullOrEmpty(item.OwnerRef))
  146. throw new PSQueueValidationException(PSQueueValidationException.ErrorCodes.OwnerRefRequired);
  147. var provider = ResolveProvider(item.ProcessProviderClass, item.Arguments);
  148. provider.Validate();
  149. if (item.AtteptsInitial <= 0);
  150. throw new PSQueueValidationException(PSQueueValidationException.ErrorCodes.InitialAttemptMustBePositive);
  151. }
  152. private void SetItemStatus(IPsQueueItem item, PsStatusEnum? newStatus, bool isTransient, string message)
  153. {
  154. var oldStatus = item.Status;
  155. var oldIsTransient = item.IsStatusTransient;
  156. if (!isTransient && newStatus == null)
  157. throw new PSQueueValidationException(PSQueueValidationException.ErrorCodes.InvalidStatusTransientCombination);
  158. }
  159. private IPsProcessProvider ResolveProvider(string providerClass, IEnumerable<ITypedArgument> arguments)
  160. {
  161. if (string.IsNullOrEmpty(providerClass))
  162. throw new PSQueueValidationException(PSQueueValidationException.ErrorCodes.ProcessProviderRequired);
  163. var providerType = Type.GetType(providerClass);
  164. if (providerType == null)
  165. throw new PSQueueValidationException(PSQueueValidationException.ErrorCodes.ProcessProviderCannotResolve, providerClass);
  166. var provider = Activator.CreateInstance(providerType, arguments) as IPsProcessProvider;
  167. if (provider == null)
  168. throw new PSQueueValidationException(PSQueueValidationException.ErrorCodes.ProcessProviderCannotResolve, providerClass);
  169. return provider;
  170. }
  171. protected override void OnDisposing()
  172. {
  173. base.OnDisposing();
  174. ProcessItemStatusChanged = null;
  175. _storage.Dispose();
  176. //TODO: Implement OnDisposing
  177. }
  178. public void Start()
  179. {
  180. throw new NotImplementedException();
  181. }
  182. public void Stop()
  183. {
  184. throw new NotImplementedException();
  185. }
  186. public string EnQueueItem(IPsQueueItemDescriptor item)
  187. {
  188. throw new NotImplementedException();
  189. }
  190. public IPsQueueItem GetQueueItem(string itemId)
  191. {
  192. throw new NotImplementedException();
  193. }
  194. public void ForceStartQueueItem(string itemId, string emitter)
  195. {
  196. throw new NotImplementedException();
  197. }
  198. public void ForceStopQueueItem(string itemId, string emitter)
  199. {
  200. throw new NotImplementedException();
  201. }
  202. public void PauseQueueItem(string itemId, string emmiter)
  203. {
  204. throw new NotImplementedException();
  205. }
  206. public void StartQueueItem(string itemId, string emmiter, bool isImmediate)
  207. {
  208. throw new NotImplementedException();
  209. }
  210. public IQueryable<IPsQueueItem> QueryQueueItems(IPaging paging, string? owner, string? ownerRef, bool includeTransient = false, params PsStatusEnum[] statuses)
  211. {
  212. throw new NotImplementedException();
  213. }
  214. #endregion
  215. }
  216. }