public override string CreateSaga(MessageContext context, SagaEntity sagaentity) { try { var partition = $"{context.DateTimeUtc.ToString("yyyyMMdd")}_{context.Saga.Name}"; var id = $"{partition}@{sagaentity.Key}@{_currenttablenamesufix}"; var table = GetCloudTable(_connectionstring, $"{_sagastoragename}{_currenttablenamesufix}"); var record = new SagaRecord(partition, sagaentity.Key) { Data = sagaentity.Data, Created = sagaentity.Created, Updated = sagaentity.Updated, Name = sagaentity.Name, DataType = sagaentity.DataType, Timeout = sagaentity.Timeout, Status = sagaentity.Status }; table.Execute(TableOperation.Insert(record)); return(id); } catch (Exception ex) { throw new ApplicationException($"Error during the saga record creation saga {context.Saga.Name}", ex); } }
public override MessageEntity[] GetMessagesBySaga(SagaEntity sagaentity, string messagestoragename = "") { var serializer = _factory.Create <IMessageSerializer>(_configuration.MessageSerializerType); var table = GetCloudTable(_connectionstring, $"{_messagestorgename}{_currenttablenamesufix}"); if (!string.IsNullOrWhiteSpace(messagestoragename)) { table = GetCloudTable(_connectionstring, $"{messagestoragename}"); } var records = table.CreateQuery <MessageRecord>().Where(x => x.PartitionKey == sagaentity.Key); return(records.Select(x => new MessageEntity() { Content = x.Content, ContentType = x.ContentType, Id = x.Id, Version = x.Version, RetryCount = x.RetryCount, LastRetry = x.LastRetry, Origin = serializer.Deserialize <Origin>(x.Origin), Saga = serializer.Deserialize <SagaContext>(x.Saga), Headers = serializer.Deserialize <Dictionary <string, string> >(x.Headers), DateTimeUtc = x.DateTimeUtc, Data = x.Data, Name = x.Name }).ToArray()); }
public async Task SaveAsync <T>(T saga) where T : ISaga { var sagaType = saga.GetType().FullName; var entity = await db.Sagas.FindAsync(saga.Id, sagaType); if (entity == null) { entity = new SagaEntity { Id = saga.Id, CorrelationId = saga.CorrelationId, Type = sagaType, Completed = saga.Completed, Payload = sagaSerializer.Serialize(saga) }; db.Sagas.Add(entity); } else { entity.Payload = sagaSerializer.Serialize(saga); } db.SaveChanges(); await PutCommandsToBus(entity, saga); }
public override string CreateSaga(MessageContext context, SagaEntity sagaentity) { try { var partition = $"{context.DateTimeUtc.ToString("yyyyMMdd")}_{context.Saga.Name}"; var id = $"{partition}@{sagaentity.Key}@{_currenttablenamesufix}"; var table = GetCloudTable(_connectionstring, $"{_sagastoragename}{_currenttablenamesufix}"); var record = new SagaRecord(partition, sagaentity.Key) { Data = sagaentity.Data, Created = sagaentity.Created, Updated = sagaentity.Updated, Name = sagaentity.Name, DataType = sagaentity.DataType, Timeout = sagaentity.Timeout, Status = sagaentity.Status }; var result = table.ExecuteAsync(TableOperation.Insert(record)).GetAwaiter().GetResult(); return(id); } catch (StorageException s) { throw new ApplicationException($"Error during the saga record creation for saga {context.Saga.Name} and route {context.Route.Name}, status code: {s.RequestInformation?.HttpStatusCode} error code: {s.RequestInformation?.ExtendedErrorInformation?.ErrorCode} error message: {s.RequestInformation?.ExtendedErrorInformation?.ErrorMessage}", s); } catch (Exception ex) { throw new ApplicationException($"Error during the saga record creation for saga {context.Saga.Name} and route {context.Route.Name}", ex); } }
public override MessageEntity[] GetMessagesBySaga(SagaEntity sagaentity, string messagestoragename = "") { var serializer = _factory.Create <IMessageSerializer>(_configuration.MessageSerializerType); var table = GetCloudTable(_connectionstring, $"{_messagestorgename}{_currenttablenamesufix}"); if (!string.IsNullOrWhiteSpace(messagestoragename)) { table = GetCloudTable(_connectionstring, $"{messagestoragename}"); } var where = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, sagaentity.Key); var query = new TableQuery <MessageRecord>().Where(where); var records = ExecuteQuery <MessageRecord>(table, query); return(records.Select(x => new MessageEntity() { Content = x.Content, ContentType = x.ContentType, Identity = string.IsNullOrWhiteSpace(x.Identity) ? null: serializer.Deserialize <Identity>(x.Identity), Version = x.Version, RetryCount = x.RetryCount, LastRetry = x.LastRetry, Origin = string.IsNullOrWhiteSpace(x.Origin) ? null : serializer.Deserialize <Origin>(x.Origin), Saga = string.IsNullOrWhiteSpace(x.Saga) ? null : serializer.Deserialize <SagaContext>(x.Saga), Headers = string.IsNullOrWhiteSpace(x.Headers) ? null : serializer.Deserialize <Dictionary <string, string> >(x.Headers), DateTimeUtc = x.DateTimeUtc, Data = x.Data, Name = x.Name, ContentId = x.ContentId }).ToArray()); }
public override void UpdateSaga(MessageContext context, string sagaid, SagaEntity sagaentity) { try { var tablenamesufix = GetTableNameSufix(sagaid); var partitionkey = GetPartitionKey(sagaid); var rowkey = GetRowKey(sagaid); if (!string.IsNullOrWhiteSpace(tablenamesufix) && !string.IsNullOrWhiteSpace(partitionkey) && !string.IsNullOrWhiteSpace(rowkey)) { var table = GetCloudTable(_connectionstring, $"{_sagastoragename}{tablenamesufix}"); var result = table.ExecuteAsync(TableOperation.Retrieve <SagaRecord>(partitionkey, rowkey)).GetAwaiter().GetResult(); var record = result.Result as SagaRecord; if (record != null) { record.Data = sagaentity.Data; record.Updated = sagaentity.Updated; if (!string.IsNullOrWhiteSpace(sagaentity.Status)) { record.Status = sagaentity.Status; } record.Duration = sagaentity.Duration; if (sagaentity.Ended != null) { record.Ended = sagaentity.Ended; } record.ETag = "*"; table.ExecuteAsync(TableOperation.Replace(record)).GetAwaiter().GetResult(); } else { throw new ApplicationException($"Record not found for saga id {sagaid}"); } } else { throw new ApplicationException($"Invalid saga id format {sagaid}"); } } catch (StorageException s) { throw new ApplicationException($"Error during the saga update for saga {context.Route.Name} and route {context.Route.Name}, status code: {s.RequestInformation?.HttpStatusCode} error code: {s.RequestInformation?.ExtendedErrorInformation?.ErrorCode} error message: {s.RequestInformation?.ExtendedErrorInformation?.ErrorMessage}", s); } catch (Exception ex) { throw new ApplicationException($"Error during the saga update for saga {context.Route.Name} and route {context.Route.Name}", ex); } }
protected void SaveSaga(MessageContext messagecontext, ISagaStorage storage, SagaEntity sagaentity, IMessageSerializer serializer, object data) { sagaentity.Data = serializer.Serialize(data); sagaentity.Status = messagecontext.SagaContext.Status; storage.UpdateSaga(messagecontext, messagecontext.SagaContext.Id, sagaentity); }
internal Saga CreateSaga(SagaEntity sagaEntity) { var saga = new Saga(snowflakeIdGenerator, serviceProvider, eventSender, poleSagasOption, serializer, activityFinder, sagaEntity.Id); foreach (var activity in sagaEntity.ActivityEntities.OrderBy(m => m.Order)) { saga.AddActivity(activity.Id, activity.Name, activity.Status, activity.ParameterData, activity.Order, activity.CompensateTimes, activity.OvertimeCompensateTimes); } return(saga); }
private async Task GrpcGetSagasCore(CancellationToken cancellationToken) { using (var stream = sagaClient.GetSagas(new Pole.Sagas.Server.Grpc.GetSagasRequest { Limit = options.PreSagasGrpcStreamingResponseLimitCount, ServiceName = options.ServiceName })) { while (await stream.ResponseStream.MoveNext(cancellationToken)) { if (stream.ResponseStream.Current.IsSuccess) { try { var sagas = stream.ResponseStream.Current.Sagas.Select(m => { var result = new SagaEntity { Id = m.Id, }; result.ActivityEntities = m.Activities.Select(n => new ActivityEntity { CompensateTimes = n.CompensateTimes, OvertimeCompensateTimes = n.ExecuteTimes, Id = n.Id, Name = n.Name, Order = n.Order, ParameterData = n.ParameterData, SagaId = n.SagaId, Status = n.Status }).ToList(); return(result); }).ToList(); sagas.ForEach(async sagaEntity => { var saga = sagaRestorer.CreateSaga(sagaEntity); var compensateResult = await saga.CompensateWhenRetry(); if (compensateResult) { var expiresAt = DateTime.UtcNow.AddSeconds(options.CompeletedSagaExpiredAfterSeconds); await eventSender.SagaEnded(sagaEntity.Id, expiresAt); } }); } catch (Exception ex) { logger.LogError(ex, "Errors in NotEndedSagasCompensateRetryBackgroundService CompensateRetry"); } } } } }
public void Saga_entity_compatible_with_serviceinsight() { var entity = new SagaEntity { IntProperty = 42, DateProperty = new DateTime(2017, 10, 26, 13, 3, 13, DateTimeKind.Utc), NullableDateProperty = new DateTime(2013, 10, 26, 13, 3, 13, DateTimeKind.Utc), GuidProperty = Guid.Empty, StringProperty = "String", TimeProperty = new TimeSpan(1, 2, 3, 4), NullableTimeProperty = new TimeSpan(5, 6, 7, 8), NestedObjectProperty = new NestedObject { IntProperty = 1 } }; var sagaDataJson = SimpleJson.SerializeObject(entity, new SagaEntitySerializationStrategy()); var sagaDataProperties = JsonPropertiesHelper.ProcessValues(sagaDataJson); var jsonObj = SimpleJson.DeserializeObject(sagaDataJson) as JsonObject; Assert.IsNotNull(jsonObj, "SimpleJson.DeserializeObject"); foreach (var p in sagaDataProperties) { Assert.IsTrue(jsonObj.ContainsKey(p.Key), $"{p.Key} not found"); var expected = jsonObj[p.Key].ToString(); var value = p.Value; //ServiceInsight uses default ToString() implementation, so adjust for that: switch (p.Key) { case "DateProperty": case "NullableDateProperty": expected = DateTime.Parse(expected).ToUniversalTime().ToString(); break; case "NestedObjectProperty": value = p.Value.Replace(Environment.NewLine, string.Empty).Replace(" ", string.Empty).Replace(",NServiceBus", ", NServiceBus"); break; default: break; } Assert.AreEqual(expected, value, p.Key); } }
public async IAsyncEnumerable <SagasGroupEntity> GetSagas(DateTime dateTime, int limit) { using (var connection = new NpgsqlConnection(poleSagasStoragePostgreSqlOption.ConnectionString)) { var sql = $"select limit_sagas.\"Id\" as SagaId,limit_sagas.\"ServiceName\",activities.\"Id\" as ActivityId,activities.\"Order\",activities.\"Status\",activities.\"ParameterData\",activities.\"OvertimeCompensateTimes\",activities.\"CompensateTimes\",activities.\"Name\" as ActivityName from {activityTableName} as activities inner join(select \"Id\",\"ServiceName\" from {sagaTableName} where \"AddTime\" <= @AddTime and \"Status\" = '{nameof(SagaStatus.Started)}' limit @Limit ) as limit_sagas on activities.\"SagaId\" = limit_sagas.\"Id\" and activities.\"Status\" != @Status1 and activities.\"Status\" != @Status2"; var activities = await connection.QueryAsync <ActivityAndSagaEntity>(sql, new { AddTime = dateTime, Limit = limit, Status1 = nameof(ActivityStatus.Compensated), Status2 = nameof(ActivityStatus.Revoked) }); var groupedByServiceNameActivities = activities.GroupBy(m => m.ServiceName); foreach (var groupedByServiceName in groupedByServiceNameActivities) { SagasGroupEntity sagasGroupEntity = new SagasGroupEntity { ServiceName = groupedByServiceName.Key, }; var groupedBySagaIds = groupedByServiceName.GroupBy(m => m.SagaId); foreach (var groupedBySagaId in groupedBySagaIds) { SagaEntity sagaEntity = new SagaEntity { Id = groupedBySagaId.Key }; foreach (var activity in groupedBySagaId) { ActivityEntity activityEntity = new ActivityEntity { CompensateTimes = activity.CompensateTimes, OvertimeCompensateTimes = activity.OvertimeCompensateTimes, Id = activity.ActivityId, Order = activity.Order, ParameterData = activity.ParameterData, SagaId = activity.SagaId, Status = activity.Status, Name = activity.ActivityName }; sagaEntity.ActivityEntities.Add(activityEntity); } sagasGroupEntity.SagaEntities.Add(sagaEntity); } yield return(sagasGroupEntity); } } }
public override void CreateMessage(MessageContext context, string sagaid, SagaEntity sagaentity, MessageEntity messageentity) { try { var serializer = _factory.Create <IMessageSerializer>(_configuration.MessageSerializerType); var tablenamesufix = GetTableNameSufix(sagaid); var table = GetCloudTable(_connectionstring, $"{_messagestorgename}{tablenamesufix}"); var record = new MessageRecord(sagaentity.Key, $"{messageentity.Name}_{Guid.NewGuid()}") { Content = messageentity.Content, ContentType = messageentity.ContentType, Identity = serializer.Serialize(messageentity.Identity), Version = messageentity.Version, RetryCount = messageentity.RetryCount, LastRetry = messageentity.LastRetry, Origin = serializer.Serialize(messageentity.Origin), Saga = serializer.Serialize(messageentity.Saga), Headers = serializer.Serialize(messageentity.Headers), Tracks = serializer.Serialize(messageentity.Tracks), DateTimeUtc = messageentity.DateTimeUtc, Data = messageentity.Data, Name = messageentity.Name, ContentId = messageentity.ContentId, }; var size = Encoding.UTF8.GetByteCount(record.Content); if (size >= 64000) { record.Content = LimitByteLength(record.Content, 63000) + "..."; } var result = table.ExecuteAsync(TableOperation.Insert(record)).GetAwaiter().GetResult(); } catch (StorageException s) { throw new ApplicationException($"Error during the message record creation for saga {context.Saga.Name} with content {context.ContentType.FullName} and route {context.Route.Name}, status code: {s.RequestInformation?.HttpStatusCode} error code: {s.RequestInformation?.ExtendedErrorInformation?.ErrorCode} error message: {s.RequestInformation?.ExtendedErrorInformation?.ErrorMessage}", s); } catch (Exception ex) { throw new ApplicationException($"Error during the message record creation for saga {context.Saga.Name} with content {context.ContentType.FullName} and route {context.Route.Name}", ex); } }
public void Saga_entity_serializes_correctly() { var entity = new SagaEntity { IntProperty = 42, DateProperty = new DateTime(2017, 10, 26, 13, 3, 13, DateTimeKind.Utc), NullableDateProperty = new DateTime(2013, 10, 26, 13, 3, 13, DateTimeKind.Utc), GuidProperty = Guid.Empty, StringProperty = "String", TimeProperty = new TimeSpan(1, 2, 3, 4), NullableTimeProperty = new TimeSpan(5, 6, 7, 8), NestedObjectProperty = new NestedObject { IntProperty = 1 } }; var serialized = SimpleJson.SerializeObject(entity, new SagaEntitySerializationStrategy()); Approver.Verify(serialized); }
protected static MessageEntity CreateMessageEntity(MessageContext context, SagaEntity sagaentity) { return(new MessageEntity { Content = context.Content, ContentType = context.Route.ContentType.FullName, Identity = context.Identity, Version = context.Version, RetryCount = context.RetryCount, LastRetry = context.LastRetry, Origin = context.Origin, Saga = context.SagaContext, Headers = context.Headers, DateTimeUtc = context.DateTimeUtc, Data = sagaentity.Data, Name = context.Route.Name, Tracks = context.Tracks, ContentId = context.ContentId }); }
public override void CreateMessage(MessageContext context, string id, SagaEntity sagaentity, MessageEntity messageentity) { try { var serializer = _factory.Create <IMessageSerializer>(_configuration.MessageSerializerType); var tablenamesufix = GetTableNameSufix(id); var table = GetCloudTable(_connectionstring, $"{_messagestorgename}{tablenamesufix}"); var record = new MessageRecord(sagaentity.Key, $"{messageentity.Name}_{Guid.NewGuid()}") { Content = messageentity.Content, ContentType = messageentity.ContentType, Id = messageentity.Id, Version = messageentity.Version, RetryCount = messageentity.RetryCount, LastRetry = messageentity.LastRetry, Origin = serializer.Serialize(messageentity.Origin), Saga = serializer.Serialize(messageentity.Saga), Headers = serializer.Serialize(messageentity.Headers), Tracks = serializer.Serialize(messageentity.Tracks), DateTimeUtc = messageentity.DateTimeUtc, Data = messageentity.Data, Name = messageentity.Name, }; var size = Encoding.UTF8.GetByteCount(record.Content); if (size >= 64000) { record.Content = LimitByteLength(record.Content, 63000) + "..."; } table.Execute(TableOperation.Insert(record)); } catch (Exception ex) { throw new ApplicationException($"Error during the message record creation saga {context.Saga.Name} with content {context.ContentType.FullName}", ex); } }
private async Task PutCommandsToBus(SagaEntity entity, ISaga saga) { if (entity != null) { var totalCommands = saga.PendingCommands.Count; var queue = new Queue <Command>(saga.PendingCommands); var processed = new List <Command>(); while (queue.Count > 0) { try { var command = queue.Peek(); await bus.SendCommandAsync(command); saga.PendingCommands.Remove(queue.Dequeue()); } catch { if (totalCommands != queue.Count()) { entity.Payload = sagaSerializer.Serialize(saga); db.SaveChanges(); } throw; } } entity.Payload = sagaSerializer.Serialize(saga); db.SaveChanges(); } return; }
public MessageEntity[] GetMessagesBySaga(SagaEntity sagaentity, string messagestoragename = "") { var storage = _factory.Create <ISagaStorage>(_configuration.SagaStorageType); return(storage.GetMessagesBySaga(sagaentity, messagestoragename)); }
protected void SaveMessage(MessageContext messagecontext, ISagaStorage storage, SagaEntity sagaentity) { if (Configuration.Storage.SaveMessage) { try { var messageentity = CreateMessageEntity(messagecontext, sagaentity); storage.CreateMessage(messagecontext, messagecontext.SagaContext.Id, sagaentity, messageentity); } catch (Exception) { if (!Configuration.Storage.IgnoreExceptionOnSaveMessage) { throw; } } } }
public override SagaEntity GetSaga(string sagaid) { try { var entity = new SagaEntity(); var tablenamesufix = GetTableNameSufix(sagaid); var partitionkey = GetPartitionKey(sagaid); var rowkey = GetRowKey(sagaid); if (!string.IsNullOrWhiteSpace(tablenamesufix) && !string.IsNullOrWhiteSpace(partitionkey) && !string.IsNullOrWhiteSpace(rowkey)) { var table = GetCloudTable(_connectionstring, $"{_sagastoragename}{tablenamesufix}"); var result = table.ExecuteAsync(TableOperation.Retrieve <SagaRecord>(partitionkey, rowkey)).GetAwaiter().GetResult(); var record = result.Result as SagaRecord; if (record != null) { entity.Data = record.Data; entity.DataType = record.DataType; entity.Name = record.Name; entity.Created = record.Created; entity.Updated = record.Updated; entity.Ended = record.Ended; entity.Timeout = record.Timeout; entity.Status = record.Status; entity.Duration = record.Duration; entity.Key = record.RowKey; return(entity); } else { throw new ApplicationException($"Record not found for saga key {sagaid}"); } } else { throw new ApplicationException($"Invalid saga key format {sagaid}"); } } catch (StorageException s) { throw new ApplicationException($"Error during the saga record query for saga key {sagaid}, status code: {s.RequestInformation?.HttpStatusCode} error code: {s.RequestInformation?.ExtendedErrorInformation?.ErrorCode} error message: {s.RequestInformation?.ExtendedErrorInformation?.ErrorMessage}", s); } catch (Exception ex) { throw new ApplicationException($"Error during the saga record query for saga key {sagaid}", ex); } }
public override void UpdateSaga(MessageContext context, string id, SagaEntity sagaentity) { try { var tablenamesufix = GetTableNameSufix(id); var partitionkey = GetPartitionKey(id); var rowkey = GetRowKey(id); if (!string.IsNullOrWhiteSpace(tablenamesufix) && !string.IsNullOrWhiteSpace(partitionkey) && !string.IsNullOrWhiteSpace(rowkey)) { var table = GetCloudTable(_connectionstring, $"{_sagastoragename}{tablenamesufix}"); var result = table.Execute(TableOperation.Retrieve <SagaRecord>(partitionkey, rowkey)); var record = result.Result as SagaRecord; if (record != null) { record.Data = sagaentity.Data; record.Updated = sagaentity.Updated; if (!string.IsNullOrWhiteSpace(sagaentity.Status)) { record.Status = sagaentity.Status; } record.Duration = sagaentity.Duration; if (sagaentity.Ended != null) { record.Ended = sagaentity.Ended; } record.ETag = "*"; table.Execute(TableOperation.Replace(record)); } else { throw new ApplicationException($"Record not found for saga key {sagaentity.Key}"); } } else { throw new ApplicationException($"Invalid saga key format {sagaentity.Key}"); } } catch (StorageException ex) { if (ex.RequestInformation.HttpStatusCode == (int)HttpStatusCode.PreconditionFailed) { throw new ApplicationException($"Error during the saga update (Optimistic concurrency violation – entity has changed since it was retrieved) {sagaentity.Name}", ex); } else { throw new ApplicationException($"Error during the saga update ({ex.RequestInformation.HttpStatusCode}) {sagaentity.Name}", ex); } } catch (Exception ex) { throw new ApplicationException($"Error during the saga update {sagaentity.Name}", ex); } }
public virtual string CreateSaga(MessageContext context, SagaEntity sagaentity) { return(string.Empty); }
public override SagaEntity GetSaga(string id) { try { var entity = new SagaEntity() { Key = id }; var tablenamesufix = GetTableNameSufix(id); var partitionkey = GetPartitionKey(id); var rowkey = GetRowKey(id); if (!string.IsNullOrWhiteSpace(tablenamesufix) && !string.IsNullOrWhiteSpace(partitionkey) && !string.IsNullOrWhiteSpace(rowkey)) { var table = GetCloudTable(_connectionstring, $"{_sagastoragename}{tablenamesufix}"); var result = table.Execute(TableOperation.Retrieve <SagaRecord>(partitionkey, rowkey)); var record = result.Result as SagaRecord; if (record != null) { entity.Data = record.Data; entity.DataType = record.DataType; entity.Name = record.Name; entity.Created = record.Created; entity.Updated = record.Updated; entity.Ended = record.Ended; entity.Timeout = record.Timeout; entity.Status = record.Status; entity.Duration = record.Duration; entity.Key = record.RowKey; return(entity); } else { throw new ApplicationException($"Record not found for saga key {id}"); } } else { throw new ApplicationException($"Invalid saga key format {id}"); } } catch (Exception ex) { throw new ApplicationException($"Error during the saga record lookup saga key {id}", ex); } }
public virtual void CreateMessage(MessageContext context, string id, SagaEntity sagaentity, MessageEntity messageentity) { }
protected static MessageEntity CreateMessageEntity(MessageContext context, MiddlewareParameter parameter, SagaEntity sagaentity) { return(new MessageEntity { Content = context.ContentAsString, ContentType = parameter.Route.ContentType.FullName, Id = context.Id, Version = context.Version, RetryCount = context.RetryCount, LastRetry = context.LastRetry, Origin = context.Origin, Saga = context.SagaContext, Headers = context.Headers, DateTimeUtc = context.DateTimeUtc, Data = sagaentity.Data, Name = parameter.Route.Name, Tracks = context.Tracks }); }
public virtual void UpdateSaga(MessageContext context, string id, SagaEntity sagaentity) { }
public virtual MessageEntity[] GetMessagesBySaga(SagaEntity sagaentity, string messagestoragename = "") { return(null); }