Closed hanyelemary closed 4 years ago
Looks like you're using the built-in handler activator.
Could you show me how you are registering your saga handler?
Hi @mookid8000, I'm on the same team as @hanyelemary and would like to answer your question.
We were initially calling activator.Register(() => handler);
for each message type handled in the Saga, then we tried just registering the Saga itself, ie activator.Register(() => saga);
Both methods did not work.
The code below is how we manage our subscriptions, including registering the saga with RegisterCommandHandler<T>(IHandleMessages<T> handler)
public class Subscriber : ISimpleSubscriber, IDisposable
{
private readonly string inputQueueName;
private readonly string addressName;
private readonly IMemoryNetwork inMemNetwork;
private readonly ISubscriberStore subscriberStore;
private readonly BuiltinHandlerActivator activator;
public Subscriber(
string inputQueueName,
string addressName,
IMemoryNetwork inMemNetwork,
ISubscriberStore subscriberStore)
{
this.inputQueueName = inputQueueName;
this.addressName = addressName;
this.inMemNetwork = inMemNetwork;
this.subscriberStore = subscriberStore;
activator = new BuiltinHandlerActivator();
}
public void RegisterCommandHandler<T>(IHandleMessages<T> handler)
{
activator.Register(() => handler);
}
public void Initialize(ICollection<Type> subscriptionTypesCollection)
{
SubscriptionCollection = subscriptionTypesCollection;
Configure
.With(activator)
.Logging(l => l.None())
.Transport(t => t.UseInMemoryTransport(inMemNetwork.Network, inputQueueName))
.Subscriptions(s => s.StoreInMemory(subscriberStore.Store))
.Options(option =>
{
option.SimpleRetryStrategy(secondLevelRetriesEnabled:false,maxDeliveryAttempts: 1);
option.SetBackoffTimes(
TimeSpan.FromMilliseconds(50),
TimeSpan.FromMilliseconds(50),
TimeSpan.FromMilliseconds(50)
);
option.SetWorkerShutdownTimeout(TimeSpan.FromSeconds(10));
option.SetNumberOfWorkers(1);
option.SetMaxParallelism(1);
})
.Sagas(s => s.StoreInMemory())
.Routing(r =>
{
var routing = r.TypeBased();
foreach (var subscriptionType in subscriptionTypesCollection)
{
routing.Map(subscriptionType, addressName);
}
})
.Start();
foreach (var subscriptionType in SubscriptionCollection)
{
activator.Bus.Subscribe(subscriptionType).Wait();
}
}
public void Dispose()
{
activator.Dispose();
Console.WriteLine(inputQueueName + " Subscriber disposed");
}
public ICollection<Type> SubscriptionCollection { get; private set; }
}
Here's the reason why the same handler instance is being returned every time you receive a message:
public void RegisterCommandHandler<T>(IHandleMessages<T> handler)
{
activator.Register(() => handler);
}
You need to turn the argument into a handler factory, so that Rebus can create a new instance each time a message is received:
public void RegisterCommandHandler<T>(Func<IHandleMessages<T>> handlerFactory)
{
activator.Register(() => handlerFactory());
}
Hello,
We're having an issue where root properties are persisting/being cached across different Saga instances.
Here is an example. We have a Saga that is initiated by an event (PaymentReceivedEvent) and handles two other events that could mark completeness. Here is the flow at a high level.
The flow goes like this. We receive a payment. If the payment is declined (PaymentProcessedEvent), we mark the Saga as complete inside the handler for PaymentProcessedEvent. If the payment is approved, then we move on to PrintReceiptCommand and then we complete the saga. This is a much more simplified view of what we're working on (which needs to hold payment data state and correlate them once a payment is processed).
The problem we're seeing here is that when PaymentProcessedEvent carries an approval after a decline, then was marked as complete is still true from the previous transaction. Therefore, the handler for PrintReceiptCommand never gets invoked as the Saga has been deleted.
The handler result returned from the method below appears to be caching the root properties of all instances of our Saga.
Do you have any insight into this?