Rebus cannot subscribe to multiple endpoints with same 'messages' value

850 views Asked by At

Is there any reason that Rebus cannot be used in a pub/sub protocol, subscribing to multiple endpoints publishing messages from a shared assembly?

When I try to configure a Rebus subscriber with this:

<rebus inputQueue="ocs.subscriber.input" errorQueue="ocs.subscriber.error" workers="1" maxRetries="5">
  <endpoints>
    <add messages="D3A.Messages" endpoint="ocs.publisher.input" />
    <add messages="D3A.Messages" endpoint="ocs.publisher.input@osi2552" />
  </endpoints>
</rebus>

An exception is thrown at

.Transport(t => t.UseMsmqAndGetInputQueueNameFromAppConfig())

The exception thrown is this:

An unhandled exception of type 'Rebus.Configuration.ConfigurationException' occurred in Rebus.dll

Additional information: 

An error occurred when trying to parse out the configuration of the RebusConfigurationSection:


System.Configuration.ConfigurationErrorsException: The entry 'D3A.Messages' has already been added. (C:\projects\OCS.Subscriber\bin\Release\OCS.Subscriber.vshost.exe.Config line 22)

   at System.Configuration.BaseConfigurationRecord.EvaluateOne(String[] keys, SectionInput input, Boolean isTrusted, FactoryRecord factoryRecord, SectionRecord sectionRecord, Object parentResult)

   at System.Configuration.BaseConfigurationRecord.Evaluate(FactoryRecord factoryRecord, SectionRecord sectionRecord, Object parentResult, Boolean getLkg, Boolean getRuntimeObject, Object& result, Object& resultRuntimeObject)

   at System.Configuration.BaseConfigurationRecord.GetSectionRecursive(String configKey, Boolean getLkg, Boolean checkPermission, Boolean getRuntimeObject, Boolean requestIsHere, Object& result, Object& resultRuntimeObject)

   at System.Configuration.BaseConfigurationRecord.GetSectionRecursive(String configKey, Boolean getLkg, Boolean checkPermission, Boolean getRuntimeObject, Boolean requestIsHere, Object& result, Object& resultRuntimeObject)

   at System.Configuration.BaseConfigurationRecord.GetSectionRecursive(String configKey, Boolean getLkg, Boolean checkPermission, Boolean getRuntimeObject, Boolean requestIsHere, Object& result, Object& resultRuntimeObject)

   at System.Configuration.BaseConfigurationRecord.GetSection(String configKey)

   at System.Configuration.ClientConfigurationSystem.System.Configuration.Internal.IInternalConfigSystem.GetSection(String sectionName)

   at System.Configuration.ConfigurationManager.GetSection(String sectionName)

   at Rebus.Configuration.RebusConfigurationSection.LookItUp()

   at Rebus.Transports.Msmq.MsmqConfigurationExtension.UseMsmqAndGetInputQueueNameFromAppConfig(RebusTransportConfigurer configurer)

This seems a bit odd to me. It seems like a perfectly valid use case to have Rebus subscriber to multiple publishers - some sharing a common POCO assembly.

What is the reasoning behind this ... and (more importantly) is there a way to achieve this in Rebus?

And also: I was expecting Rebus to add this exception to the log (I use log4net) but it seems that exceptions thrown during configuration time of Rebus is not logged. This must be a mistake, right?

1

There are 1 answers

3
mookid8000 On BEST ANSWER

The reason why it doesn't readily work, is that it is assumed that each message type belongs to ("is owned by") one logical service only.

From the names of your publishers I assume that they're the same - i.e. that they're two instances of the same logical service, which means that you can easily achieve what you're after by letting them share their subscription storage - e.g. in a shared SQL Server.

When they share their subscription storage, it doesn't matter which one receives the subscription request, and therefore, you'd only need to map the message assembly to one of the instances.

PS: Good find with the logging thing - I've created an issue and it will be fixed soon ;)

PPS: Update about the logging thing - I remember now that Rebus intentionally does NOT log error during configuration - the reasoning is that logging is done only for exceptions that don't surface to the caller, because you'll be able to catch the other exceptions and do what you think should be done with them.