Beispiel #1
0
        /// <inheritdoc />
        public async Task DispatchAsync(IBackgroundCommand command, CancellationToken cancellationToken = default)
        {
            if (command == null)
            {
                throw new ArgumentNullException(nameof(command));
            }

            try
            {
                var stopwatch = Stopwatch.StartNew();
                await _wrappedDispatcher.DispatchAsync(command, cancellationToken);

                stopwatch.Stop();
                var eventTelemetry = new EventTelemetry(BackgroundCommandDispatchedEventName);
                eventTelemetry.Properties.Add(nameof(command.Id), command.Id);
                eventTelemetry.Properties.Add(nameof(command.Timestamp), command.Timestamp.ToString("O", CultureInfo.InvariantCulture));
                eventTelemetry.Metrics.Add(BackgroundCommandDispatchTimeMetricName, stopwatch.ElapsedMilliseconds);
                _options.Value.AdditionalProperties?.Invoke(command, eventTelemetry.Properties);
                _telemetryClient.TrackEvent(eventTelemetry);
            }
            catch (Exception ex)
            {
                var exceptionTelemetry = new ExceptionTelemetry(ex);
                exceptionTelemetry.Properties.Add(nameof(command.Id), command.Id);
                exceptionTelemetry.Properties.Add(nameof(command.Timestamp), command.Timestamp.ToString("O", CultureInfo.InvariantCulture));
                _options.Value.AdditionalProperties?.Invoke(command, exceptionTelemetry.Properties);
                _telemetryClient.TrackException(exceptionTelemetry);
                throw;
            }
        }
Beispiel #2
0
        /// <inheritdoc />
        public async Task ProcessAsync(IBackgroundCommand command, CancellationToken cancellationToken = default)
        {
            if (command == null)
            {
                throw new ArgumentNullException(nameof(command));
            }

            var handlerType = typeof(IBackgroundCommandHandler <>).MakeGenericType(command.GetType());

            var handler = _services.GetService(handlerType);

            if (handler == null)
            {
                throw new BackgroundProcessingException($"Unable to find a suitable handler for command {command} ({command.GetType()}).");
            }

            var handleMethod = handlerType.GetMethod("HandleAsync", new[] { command.GetType(), typeof(CancellationToken) });

            if (handleMethod == null)
            {
                throw new BackgroundProcessingException($"Unable to find proper handle method in {handlerType}.");
            }

            await(handleMethod.Invoke(handler, new object[] { command, cancellationToken }) as Task);
        }
 public static string Schedule(this IBackgroundCommand cmd, TimeSpan delay)
 {
     if (cmd == null)
     {
         throw new ArgumentNullException(nameof(cmd));
     }
     return(BackgroundJob.Schedule(() => cmd.Invoke(cmd.Args), delay));
 }
 public static string Once(this IBackgroundCommand cmd)
 {
     if (cmd == null)
     {
         throw new ArgumentNullException(nameof(cmd));
     }
     return(BackgroundJob.Enqueue(() => cmd.Invoke(cmd.Args)));
 }
 /// <summary>
 /// Aborts any process.
 /// </summary>
 public void AbortProcess()
 {
     if (thread != null)
     {
         thread.Abort();
     }
     thread  = null;
     process = null;
 }
Beispiel #6
0
        /// <inheritdoc />
        public async Task DispatchAsync(IBackgroundCommand command, CancellationToken cancellationToken = default)
        {
            if (command == null)
            {
                throw new ArgumentNullException(nameof(command));
            }

            _queue.Enqueue(command);
            _semaphore.Release();
        }
 /// <inheritdoc />
 public async Task ProcessAsync(IBackgroundCommand command, CancellationToken cancellationToken = default)
 {
     try
     {
         await _wrappedProcessor.ProcessAsync(command, cancellationToken);
     }
     finally
     {
         _awaiter.Signal();
     }
 }
 /// <summary>
 /// Initializes a new instance of the <see cref="BackgroundCommandEvent"/> class.
 /// </summary>
 /// <param name="command">The <see cref="IBackgroundCommand"/>.</param>
 /// <param name="status">The <see cref="BackgroundCommandEventStatus"/>.</param>
 /// <param name="timestamp">The timestamp of the event.</param>
 /// <param name="exception">The <see cref="Exception"/>, if any.</param>
 public BackgroundCommandEvent(
     IBackgroundCommand command,
     BackgroundCommandEventStatus status,
     DateTimeOffset timestamp,
     Exception exception = null)
 {
     Command   = command ?? throw new ArgumentNullException(nameof(command));
     Status    = status;
     Timestamp = timestamp;
     Exception = exception;
 }
        //# Example of job definition:
        //# .----------------- minute (0 - 59)
        //# |   .------------- hour (0 - 23)
        //# |   |  .---------- day of month (1 - 31)
        //# |   |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
        //# |   |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7)
        //# |   |  |  |  |
        //# *   *  *  *  *   command to be executed
        //m/n**** command1

        //0 0/10 * * * ?  => every 10 minutes

        /// <summary>
        /// Cron.Minutely
        /// Cron.Daily
        /// ...
        /// </summary>
        /// <param name="cmd"></param>
        /// <param name="cron"></param>
        public static void Recurring(this IBackgroundCommand cmd, Func <string> cron)
        {
            if (cmd == null)
            {
                throw new ArgumentNullException(nameof(cmd));
            }
            if (cron == null)
            {
                throw new ArgumentNullException(nameof(cron));
            }
            RecurringJob.AddOrUpdate(() => cmd.Invoke(cmd.Args), cron);
        }
 /// <summary>
 /// Starts the process.
 /// </summary>
 /// <param name="process">The process to to be computed.</param>
 /// <throws cref="System.Exception">Throws if the instance is already occupied by another process.</throws>
 public void StartProcess(IBackgroundCommand process)
 {
     if (thread == null)
     {
         this.process = process;
         thread       = new Thread(process.Execute);
         thread.Start();
     }
     else
     {
         throw new System.Exception("Only one background process can be active. Join or Abort active process before starting another one.");
     }
 }
Beispiel #11
0
        /// <inheritdoc />
        public async Task DispatchAsync(IBackgroundCommand command, CancellationToken cancellationToken = default)
        {
            try
            {
                var dispatchEvent = new BackgroundCommandEvent(command, BackgroundCommandEventStatus.Dispatched, DateTimeOffset.UtcNow);
                await _wrappedDispatcher.DispatchAsync(command, cancellationToken);

                await _repository.Add(dispatchEvent);
            }
            catch (Exception ex)
            {
                await _repository.Add(new BackgroundCommandEvent(command, BackgroundCommandEventStatus.Error, DateTimeOffset.UtcNow, ex));

                throw;
            }
        }
Beispiel #12
0
        /// <inheritdoc />
        public async Task ProcessAsync(IBackgroundCommand command, CancellationToken cancellationToken = default)
        {
            try
            {
                await _repository.Add(new BackgroundCommandEvent(command, BackgroundCommandEventStatus.Processing, DateTimeOffset.UtcNow), cancellationToken);

                await _wrappedProcessor.ProcessAsync(command, cancellationToken);

                await _repository.Add(new BackgroundCommandEvent(command, BackgroundCommandEventStatus.Processed, DateTimeOffset.UtcNow));
            }
            catch (Exception ex)
            {
                await _repository.Add(new BackgroundCommandEvent(command, BackgroundCommandEventStatus.Error, DateTimeOffset.UtcNow, ex));

                throw;
            }
        }
 /// <summary>
 /// Joins the process.
 /// </summary>
 /// <throws cref="System.Exception">Throws if no process is waiting to be joined.</throws>
 public void JoinProcess()
 {
     if (thread != null)
     {
         if (GetState() == States.WaitingForJoin)
         {
             thread.Join();
             thread = null;
             process.OnJoin();
             process = null;
         }
         else
         {
             throw new System.Exception("Process cannot be joined becouse it is not finished.");
         }
     }
     else
     {
         throw new System.Exception("No process to join.");
     }
 }
        /// <inheritdoc />
        public async Task DispatchAsync(IBackgroundCommand command, CancellationToken cancellationToken = default)
        {
            if (command == null)
            {
                throw new ArgumentNullException(nameof(command));
            }

            try
            {
                var options = _options.Value;
                await _queue.AddMessageAsync(
                    new CloudQueueMessage(await _serializer.SerializeAsync(command)),
                    timeToLive : options.TimeToLive,
                    initialVisibilityDelay : options.InitialVisibilityDelay,
                    options : options.QueueRequestOptions,
                    operationContext : options.OperationContextBuilder != null?options.OperationContextBuilder(command) : null,
                    cancellationToken);
            }
            catch (Exception ex)
            {
                throw new BackgroundProcessingException($"Error while enqueueing command {command}: {ex.Message}", ex);
            }
        }
Beispiel #15
0
 /// <inheritdoc />
 public async Task <string> SerializeAsync(IBackgroundCommand command, CancellationToken cancellationToken = default)
 => JsonConvert.SerializeObject(command, _jsonSerializerSettings);
Beispiel #16
0
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            var options = _options.Value;

            var processMessagesActionBlock = new ActionBlock <CloudQueueMessage>(
                async message =>
            {
                IBackgroundCommand command = null;
                try
                {
                    command = await _serializer.DeserializeAsync(message.AsString);

                    using (var handlerRuntimeCancellationTokenSource = new CancellationTokenSource(options.MaxHandlerRuntime))
                        using (var combinedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken, handlerRuntimeCancellationTokenSource.Token))
                            using (var scope = _services.CreateScope())
                            {
                                var processor = scope.ServiceProvider.GetRequiredService <IBackgroundProcessor>();
                                await processor.ProcessAsync(command, combinedCancellationTokenSource.Token);
                                await _queue.DeleteMessageAsync(message);
                            }
                }
                catch (Exception ex)
                {
                    _logger.LogError($"An error occured while processing {command}: {ex.Message}", ex);
                }
            },
                new ExecutionDataflowBlockOptions
            {
                CancellationToken      = stoppingToken,
                BoundedCapacity        = options.DegreeOfParallelism,
                MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded,
            });

            var currentPollingFrequency = options.PollingFrequency;

            while (!stoppingToken.IsCancellationRequested)
            {
                var messages = await _queue.GetMessagesAsync(
                    messageCount : options.MessagesBatchSize,
                    visibilityTimeout : options.MaxHandlerRuntime.Add(options.HandlerCancellationGraceDelay),
                    options : options.QueueRequestOptions,
                    operationContext : options.OperationContextBuilder != null?options.OperationContextBuilder() : null);

                if (!messages.Any())
                {
                    await Task.Delay(currentPollingFrequency);

                    currentPollingFrequency = options.NextPollingFrequency(currentPollingFrequency);
                    continue;
                }

                currentPollingFrequency = options.PollingFrequency;

                foreach (var message in messages)
                {
                    await processMessagesActionBlock.SendAsync(message, stoppingToken);
                }
            }

            processMessagesActionBlock.Complete();
            await processMessagesActionBlock.Completion;
        }