JasperFx / marten

.NET Transactional Document DB and Event Store on PostgreSQL
https://martendb.io
MIT License
2.75k stars 429 forks source link

DuplicateSubscriptionNamesException: Bug or Feature? #3231

Closed dvlprx closed 2 months ago

dvlprx commented 2 months ago

We would use the following configuration, to have each instance of the subscription publish different events to different external system.

This code throws a DuplicateSubscriptionNamesException, although we expected it to work, since every instance has it's own unique subscriptionName:

// publish all events of "Invoice" streams to an external service
.AddSubscriptionWithServices<MartenSubscriptionTest>(ServiceLifetime.Singleton, o =>
{
    o.SubscriptionName = "TestSubscription_1";
    o.FilterIncomingEventsOnStreamType(typeof(Invoice));
    o.Options.SubscribeFromPresent();
})

// publish all events of "User" streams to another external service
.AddSubscriptionWithServices<MartenSubscriptionTest>(ServiceLifetime.Singleton, o =>
{
    o.SubscriptionName = "TestSubscription_2";
    o.FilterIncomingEventsOnStreamType(typeof(User));
    o.Options.SubscribeFromPresent();
})

Sadly there is no overload, that accepts a factory.

jeremydmiller commented 2 months ago

I'd say that it's a bug, but it's also not exactly a use case I'd considered. Might be an "I take pull requests" issue for you

Might be a timing issue w/ the nested closure and when that's applied

dvlprx commented 2 months ago

Thanks for clarification. I will not start hunting timing-issues in OSS projects, that I'm not fully familiar with.

Maybe you just want to update documentation, telling that a subscription-TYPE can only be used once in the whole system.

jeremydmiller commented 2 months ago

@dvlprx "Maybe you just want to update documentation, telling that a subscription-TYPE can only be used once in the whole system." -- also a good pull request idea for a community project.

The easy workaround of course is to just use two different types for two different subscriptions.

dvlprx commented 2 months ago

The easy workaround of course is to just use two different types for two different subscriptions.

In our case that is not an options, because it would lead to an unjustifiable amount of projects, to isolate the dependency to marten, just to derive subscriptions for each specific projection-type.

With the above attempt, we could have had a single configurable "exporter-subscription", that is built eg via reflection and feeds our external projections with the already correct events, while using marten's built in progression/retry/asyncDaemon mechanisms as motor for the external projections.

dvlprx commented 2 months ago

Found a workaround, by using a generic MartenSubscriptionTest< T > in the above sample, with T being the external interface