Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sagas Doesn't Work #50

Closed
ibraheem-ghabash opened this issue Jun 30, 2021 · 16 comments
Closed

Sagas Doesn't Work #50

ibraheem-ghabash opened this issue Jun 30, 2021 · 16 comments

Comments

@ibraheem-ghabash
Copy link

Hi Charles,

Trying to use sagas with persistent storage but it doesn't work, so I created a simple project where I tried using both scenarios: using Aggregates.NET's Apply and using NServiceBus's native context.Publish, in the native scenario the saga works fine, but with Apply scenario the saga never gets executed (same configurations in both scenarios)

Here you can find the project I created for this test.

Thank you

@AbdoDabbas
Copy link

@charlessolar Hi Charels,
Any idea how to fix this ?
The way you mentioned here:
https://github.com/charlessolar/Aggregates.NET/tree/master/samples/5.%20Command%20Saga
Is not the same as NServiceBus uses, is it?

@charlessolar
Copy link
Owner

Looking at this briefly it seems the sample app is using NSBs sagas yes.

Agg.net sagas are meant to be described as a series of commands like the example you linked.
Screenshot_20210704-200457

I'll look deeper into this tomorrow!

@AbdoDabbas
Copy link

Thanks @charlessolar,
Our use case is, we need to have series of commands but the handlers distributed on multiple services, I think the sample in Agg.Net is meant for commands handled in the same service.

@ibraheem-ghabash
Copy link
Author

Hi @charlessolar

I have just pushed a new change to my original repository with applying the solution you proposed (you can find the exact code here )

But we still have an exception (short message):
System.Data.SqlClient.SqlException (0x80131904): Invalid object name 'dbo.Sales_CommandSagaHandler'.
at System.Data.SqlClient.SqlCommand.<>c.b__126_0(Task`1 result)

@charlessolar
Copy link
Owner

I believe that exception is from your library - are you using Sql Transport by chance?

There isn't anything wrong with how you tried to do the saga before and for your design it may be preferable.

Can you setup your example based off of the helloworld sample? https://github.com/charlessolar/Aggregates.NET/tree/master/samples/1.%20HelloWorld

That way I can run it myself and see why your saga is not properly running

@AbdoDabbas
Copy link

@charlessolar I was able to figure out what's missing, I need directly to add a reference for the NServiceBus.Persistence.Sql nuget package in the project that's handling the saga and triggering it.
But now a new issue is showing:

Cannot set the NServiceBus.ConversationId header to '57bd6679-c25a-4113-a604-ad610158515e' as it cannot override the incoming header value ('57bd6679-c25a-4113-a604-ad610158515e').

Here's the detailed message:

2021-07-10 23:53:49.882 ERROR Moving message '1fab3ff2-f55b-4e86-a583-ad6101585158' to the error queue 'error' because processing failed due to an exception:
System.Exception: Cannot set the NServiceBus.ConversationId header to '57bd6679-c25a-4113-a604-ad610158515e' as it cannot override the incoming header value ('57bd6679-c25a-4113-a604-ad610158515e').
at NServiceBus.AttachCausationHeadersBehavior.SetConversationIdHeader(IOutgoingLogicalMessageContext context, IncomingMessage incomingMessage)
at NServiceBus.AttachCausationHeadersBehavior.Invoke(IOutgoingLogicalMessageContext context, Func2 next) at (Closure2 , IOutgoingLogicalMessageContext )
at NServiceBus.Pipeline.Behavior1.<>c__DisplayClass0_0.<Invoke>b__0() at Aggregates.Internal.MutateOutgoing.Invoke(IOutgoingLogicalMessageContext context, Func1 next) in C:\projects\aggregates-net\src\Aggregates.NET.NServiceBus\Internal\MutateOutgoing.cs:line 56
at NServiceBus.Pipeline.Behavior1.Invoke(TContext context, Func2 next)
at (Closure2 , IOutgoingLogicalMessageContext ) at NServiceBus.MutateOutgoingMessageBehavior.Invoke(IOutgoingLogicalMessageContext context, Func2 next)
at (Closure2 , IOutgoingLogicalMessageContext ) at NServiceBus.MulticastPublishConnector.Invoke(IOutgoingPublishContext context, Func2 stage)
at (Closure2 , IOutgoingPublishContext ) at NServiceBus.EnforcePublishBestPracticesBehavior.Invoke(IOutgoingPublishContext context, Func2 next)
at (Closure2 , IOutgoingPublishContext ) at NServiceBus.Pipeline1.Invoke(TContext context)
at NServiceBus.MessageOperations.Publish(IBehaviorContext context, Type messageType, Object message, PublishOptions options)
at NServiceBus.MessageOperations.Publish(IBehaviorContext context, Object message, PublishOptions options)
at NServiceBus.IncomingContext.Publish(Object message, PublishOptions options)
at NServiceBus.IPipelineContextExtensions.Publish(IPipelineContext context, Object message)
at TestProj.MyHandler.Handle(MyClass message, IMessageHandlerContext context) in C:\Projects\TestProj\MyHandler.cs:line 63
at Aggregates.Internal.BulkInvokeHandlerTerminator.Terminate(IInvokeHandlerContext context) in C:\projects\aggregates-net\src\Aggregates.NET.NServiceBus\Internal\BulkInvokeHandlerTerminator.cs:line 88
at NServiceBus.SagaPersistenceBehavior.Invoke(IInvokeHandlerContext context, Func2 next) at NServiceBus.LoadHandlersConnector.Invoke(IIncomingLogicalMessageContext context, Func2 stage)
at CurrentSessionBehavior.Invoke(IIncomingLogicalMessageContext context, Func1 next) in /_/src/SqlPersistence/SynchronizedStorage/CurrentSessionBehavior.cs:line 19 at NServiceBus.ScheduledTaskHandlingBehavior.Invoke(IIncomingLogicalMessageContext context, Func2 next)
at NServiceBus.InvokeSagaNotFoundBehavior.Invoke(IIncomingLogicalMessageContext context, Func2 next) at Aggregates.Internal.LogContextProviderBehaviour.Invoke(IIncomingLogicalMessageContext context, Func1 next) in C:\projects\aggregates-net\src\Aggregates.NET.NServiceBus\Internal\LogContextProviderBehaviour.cs:line 39
at Aggregates.Internal.LocalMessageUnpack.Invoke(IIncomingLogicalMessageContext context, Func1 next) in C:\projects\aggregates-net\src\Aggregates.NET.NServiceBus\Internal\LocalMessageUnpack.cs:line 110 at Aggregates.Internal.UnitOfWorkExecutor.Invoke(IIncomingLogicalMessageContext context, Func1 next) in C:\projects\aggregates-net\src\Aggregates.NET.NServiceBus\Internal\UnitOfWorkExecutor.cs:line 90
at Aggregates.Internal.UnitOfWorkExecutor.Invoke(IIncomingLogicalMessageContext context, Func1 next) in C:\projects\aggregates-net\src\Aggregates.NET.NServiceBus\Internal\UnitOfWorkExecutor.cs:line 129 at ICS.Shared.Infrastructure.Behaviours.CommandExceptionBehaviour.Invoke(IIncomingLogicalMessageContext context, Func1 next)
at Aggregates.Internal.ExceptionRejector.Invoke(IIncomingLogicalMessageContext context, Func1 next) in C:\projects\aggregates-net\src\Aggregates.NET.NServiceBus\Internal\ExceptionRejector.cs:line 49 at Aggregates.Internal.ExceptionRejector.Invoke(IIncomingLogicalMessageContext context, Func1 next) in C:\projects\aggregates-net\src\Aggregates.NET.NServiceBus\Internal\ExceptionRejector.cs:line 99
at Aggregates.Internal.SagaBehaviour.Invoke(IIncomingLogicalMessageContext context, Func1 next) in C:\projects\aggregates-net\src\Aggregates.NET.NServiceBus\Internal\SagaBehaviour.cs:line 32 at Aggregates.Internal.CommandAcceptor.Invoke(IIncomingLogicalMessageContext context, Func1 next) in C:\projects\aggregates-net\src\Aggregates.NET.NServiceBus\Internal\CommandAcceptor.cs:line 41
at NServiceBus.DeserializeMessageConnector.Invoke(IIncomingPhysicalMessageContext context, Func2 stage) at NServiceBus.InvokeAuditPipelineBehavior.Invoke(IIncomingPhysicalMessageContext context, Func2 next)
at NServiceBus.ProcessingStatisticsBehavior.Invoke(IIncomingPhysicalMessageContext context, Func2 next) at NServiceBus.TransportReceiveToPhysicalMessageConnector.Invoke(ITransportReceiveContext context, Func2 next)
at NServiceBus.MainPipelineExecutor.Invoke(MessageContext messageContext)
at Aggregates.Internal.Dispatcher.SendLocal(IFullMessage message, IDictionary`2 headers) in C:\projects\aggregates-net\src\Aggregates.NET.NServiceBus\Internal\Dispatcher.cs:line 117
Exception details:
Message ID: 1fab3ff2-f55b-4e86-a583-ad6101585158

@AbdoDabbas
Copy link

Hi @charlessolar,
I needed to go around the issue of saga I mentioned before, so I did the following:
"Aggregates.Configuration.Build":

var config = new EndpointConfiguration("EndpointName");
endpointConfig.BusConfiguration.Pipeline.Remove("MutateOutgoing");

I'm trying now to do the following:
EndpointA -- CommandA To (received in B) -->
EndpointB -- EventB triggered from the "Apply" method To (received in A) -->
EndpointA (Here the event won't be received in A)

For some reason I'm not receiving the last event in EndpointA.

@charlessolar
Copy link
Owner

Hey @AbdoDabbas

Looking over the issue from above its actually a small bug / possible issue with NSB itself.
Its complaining that it can't change a header to a value that is the current value...

Removing the MutateOutgoing step shouldn't stop this issue though.. since its the NServiceBus.AttachCausationHeadersBehavior step that causes it. Most likely some header Agg.Net is setting in Mutate is redundant so I'll open a new ticket for this.

Would be nice if NSB checked if the header was there and equal before throwing but thats not important.

As for your question does EndpointA connect to the eventstore? Check the eventstore UI to see if theres a projection setup for your endpoint which contains the event you're interested in

@AbdoDabbas
Copy link

AbdoDabbas commented Aug 4, 2021

Regarding the example, yes both endpoints are connected to EventStore, I checked the UI and there's a projection for EventB on EndpointB.

I'm still using Aggregates.NET version 0.15.67.918, upgrading will be hard for me now.

EventB (which is generated by "Apply") is not even registered in EventStore, I can't see it, not even in NserviceBus audit app (ServiceInsight, which reads from RabbitMQ and shows the events and commands linked to each other), it looks like it disappeared.

Any suggestions?

@charlessolar
Copy link
Owner

Can you generate a debug log file? Agg.net uses LibLog so any logger supported by LibLog will receive Agg.net logs.

Once an event is applied the only way its wont be committed to the store would be if theres an error processing the command.

@AbdoDabbas
Copy link

Any sample how to do that? I checked the Saga sample, and added Serilog, no errors showed at all, just the regular warning that I should use the "Versioned" attribute.

@charlessolar
Copy link
Owner

If you're using serilog for logging then thats the way to go.

Check Hello World

Log.Logger = new LoggerConfiguration()

And add a line for logging to a file .WriteTo.File("C:/Logs/log.txt") something like that

All that debug info should help determine why your event is not getting to the eventstore

@AbdoDabbas
Copy link

I added a logger and used verbose level.
Nothing showed as error, all are just debug and info that the event I'm watching is there and linked to a handler and all good.
No info that the event triggered at all.

Let me re-describe the sample with the new names I just copied and pasted to prepare the example:
ClientUI -> Command PlaceOrder -> Sales.
Sales -> Apply event OrderPlaced -> Billing.
Billing -> Apply event ToOrderEndpointEvent -> Sales.

ToOrderEndpointEvent is the event that's not shown in the logs and not reaching the Sales endpoint.

Here're the logs:
billing-logs.txt
sales-logs.txt

@charlessolar
Copy link
Owner

charlessolar commented Aug 9, 2021

Ok I think I know what the problem is then from your description.

Event handlers cannot write events to the store. Only commands can generate events which are written to the eventstore.

Your event handlers typically do not have access to the eventstore streams only to the projected events.

For whatever purpose you want Billing to communicate back to Sales you need to send a command instead.

Try this:

ClientUI -> Command PlaceOrder -> Sales
Sales -> Apply event OrderPlaced -> Billing, Warehouse
Billing -> Command FinalizeOrder -> Sales
Sales -> Apply event OrderFinalized -> Warehouse
Sales -> Command ReleaseOrder -> Warehouse
Warehouse -> Apply event OrderShipped -> Sales

Something like that to represent a order flow. I added Warehouse in there to show how different endpoints can care about different events and how to process commands vs events.

Have you seen the eShop example that I made? https://github.com/charlessolar/eShopOnContainersDDD

It contains a pretty good starting point for a "shop" type app

@AbdoDabbas
Copy link

I want to do this as a workaround because the Saga as mentioned in NSB documentations didn't work for me (but not using the context to publish events, I thought I can depend on ES events to use Saga).
So the idea is to do things in specific order.

I will check the eShop sample and try what you suggested.

Thanks.

charlessolar added a commit that referenced this issue Jan 5, 2022
@charlessolar
Copy link
Owner

Saga's and the header issue were upgraded and fixed in v0.17!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants