Beispiel #1
0
        /// <inheritdoc/>
        public Task SaveSaga(object saga, QueueContext context)
        {
            var sagaType     = saga.GetType();
            var nameProperty = sagaType.GetProperty("SagaName");
            var sagaName     = (string)nameProperty.GetValue(saga);

            // if the saga is complete, we can delete the data.
            var  completeProperty = sagaType.GetProperty("SagaComplete");
            bool IsComplete       = completeProperty.GetValue(saga) is bool completeValue && completeValue;

            if (IsComplete)
            {
                return(_dataAccess.DeleteSagaData(sagaName, context.SagaKey));
            }

            // the saga is not complete, serialize it.
            var dataProperty = sagaType.GetProperty("Data");
            var dataObject   = dataProperty.GetValue(saga);

            if (dataObject == null)
            {
                dataObject = Activator.CreateInstance(dataProperty.PropertyType);
            }
            var serializedData = _serializer.SerializeSaga(dataObject, dataProperty.PropertyType);

            if (context.SagaData == null)
            {
                // we have never persisted saga data for this instance (Message was a saga start message).
                // create a new row in the DB.
                context.SagaData = new SagaData
                {
                    SagaId = Guid.NewGuid(),
                    Key    = context.SagaKey,
                    Data   = serializedData
                };

                // if two start messages are processed at the same time, two inserts could occur on different threads.
                // if that happens, the second insert is expected to throw a duplicate key constraint violation.
                return(_dataAccess.Insert(context.SagaData, sagaName));
            }
            else
            {
                // update the existing row.
                context.SagaData.Data = serializedData;
                return(_dataAccess.Update(context.SagaData, sagaName));
            }
        }
Beispiel #2
0
 /// <inheritdoc/>
 public Task Fail(QueueContext context, Exception exception)
 {
     context.MessageData.Retries++;
     context.MessageData.NotBefore    = _clock.UtcNow.AddSeconds(5 * context.MessageData.Retries); // Wait longer between retries.
     context.Headers.ExceptionDetails = exception.ToString();
     context.MessageData.Headers      = _serializer.SerializeHeaders(context.Headers);
     if (context.MessageData.Retries >= MaxRetries)
     {
         _log.Error($"Message {context.MessageData.MessageId} exceeded max retries ({MaxRetries}) and has failed.");
         context.MessageData.Failed = DateTime.UtcNow;
         _counters.FailMessage();
         return(_dataAccess.FailMessage(context.MessageData, context.SourceQueue));
     }
     else
     {
         _log.Error($"Message {context.MessageData.MessageId} will be retried at {context.MessageData.NotBefore}.");
         _counters.RetryMessage();
         return(_dataAccess.Update(context.MessageData, context.SourceQueue));
     }
 }
Beispiel #3
0
        /// <inheritdoc/>
        public async Task LoadSaga(object saga, QueueContext context)
        {
            // work out the class name of the saga.
            var sagaType     = saga.GetType();
            var nameProperty = sagaType.GetProperty("SagaName");
            var sagaName     = (string)nameProperty.GetValue(saga);

            // fetch the data from the DB.
            context.SagaData = await _dataAccess.GetSagaData(sagaName, context.SagaKey);

            if (context.SagaData != null && context.SagaData.Blocked)
            {
                return;
            }

            // Hypothetically locked could be false, and sagadata could be null if the saga hasn't been started.
            // and if two saga starts are processed at the same time, a second insert will occur and
            // that will fail with a duplicate key constraint.

            // determine the type to deserialze to or create.
            var dataProperty = sagaType.GetProperty("Data");
            var sagaDataType = dataProperty.PropertyType;

            object dataObject;

            if (context.SagaData == null)
            {
                // no data in the DB, create a new object.
                dataObject = Activator.CreateInstance(sagaDataType);
            }
            else
            {
                // deserialize
                // try catch needed? Probably better to throw and let the error handling deal with it.
                // Someone may have to fix the saga data and retry the failed message though.
                dataObject = _serializer.DeserializeSaga(context.SagaData.Data, sagaDataType);
            }

            // assign the data to the saga.
            dataProperty.SetValue(saga, dataObject);
        }
Beispiel #4
0
 /// <inheritdoc/>
 public Task DelayMessage(QueueContext messageContext, int milliseconds)
 {
     messageContext.MessageData.NotBefore = _clock.UtcNow.AddMilliseconds(milliseconds);
     _counters.DelayMessage();
     return(_dataAccess.Update(messageContext.MessageData, messageContext.SourceQueue));
 }
Beispiel #5
0
 /// <inheritdoc/>
 public Task Complete(QueueContext messageContext)
 {
     messageContext.MessageData.Completed = _clock.UtcNow;
     _counters.CompleteMessage();
     return(_dataAccess.CompleteMessage(messageContext.MessageData, messageContext.SourceQueue));
 }