On 3/12/26 22:08, John wrote:
Hi Tim,

I'm writing back again to talk about this issue more.

Per our prior conversation, we've confirmed that, with Artemis (not
ActiveMQ) for Shared Consumers, using a single connection and setting the
nms.clientId parameter in the URI, any Shared Consumer created after the
first one throws an exception ("The key already existed in the
dictionary"). This same exception is thrown under the same circumstances
when the Client ID is not on the URI and is instead generated by the
library.

I'm not 100% clear on what you did but this sounds like a bug in NMS.AMQP if the application set a client ID on a single connection and then tried to create multiple consumers on that **same** connection for the shared subscription that ought to work. I don't know much about the NMS client so I can't really help on that front.  You could conform this works by trying the same thing with Qpid JMS and it would work, I just did it locally and had no issue.


And anyway, even if it would let us, using a single connection would not
work for us. This is because we have several services that subscribe to a
topic. One of the services takes a bit of time to process the messages from
the topic. For this particular service, we want to scale out to distribute
the message workload. And, technically, we want this ability for each
service. Also, using a queue for this service is not ideal as it defeats
the purpose of having a topic.
I'm not clear on your use case so can't fully say what will work for you but if you are intending to use shared subscriptions so that any given message sent to a topic is only sent to a single client for processing via a shared subscription then those clients are treating it as a Queue, that's what Queues do. Internally the broker implements shared subscriptions as Queues attached to an multicast address.

Also, in our examination, the Client ID does not appear to be a shared
namespace, per se, as discussed earlier. Or, to clarify further, if the
Client ID is not assigned to the connections via nms.clientId, the
library-generated Client IDs are unique for each Shared Consumer's
connection and have the following format: "ID:HOSTNAME:UUID:N", where UUID
is unique for each connection.
This is odd regarding the earlier discussion, since the generated Client
IDs are unique. Why would our unique Client ID via nms.clientId be treated
any differently?

You can set unique client IDs for each connection, but you cannot set a single unique client ID for all connections, JMS prohibits that. The client ID generated by NMS.AMQP looks to be unique to each connection which is fine, and is distinct from how the client deals with shared subscriptions at the implementation where it uses its knowledge of whether you've set a client ID yourself or its using an internally generated one just for the sake of allowing the remote to have some identifying data about an incoming connection.  When it creates a shared or durable shared subscriptions it must check if you've explicitly set a client ID and use that knowledge to tag the AMQP link with properties that indicate if the subscription is globally shared (no-client ID) or privately shared (explicit client ID).


Nevertheless, as far as we can tell, the only way Shared Consumers work
correctly is without setting our own Client IDs. But it would be
advantageous for us to use our own Client IDs, and it seems we should be
able to do so. And if so, we are back to the idea that this is a bug.

You are confusing how you would like it to work with how the JMS specification says it must work which means you likely need to review the JMS specification and maybe rethink or reshape your solution in terms of what is possible within the bounds of the specification defined behavior. It does sound like you might have run into a bug in NMS.AMQP in terms of its own internal handling of shared subs which you could report but that won't help you with the main issue at hand as you've already noted.



Please let me know what your thoughts are.

Thanks

John














On Tue, Mar 10, 2026 at 9:48 PM John <[email protected]> wrote:

Thank you for the explanation. I better understand now. And I think
switching to regular consumers will probably work.

Thanks again.

On Tue, Mar 10, 2026 at 7:53 PM Timothy Bish <[email protected]> wrote:

On 3/10/26 21:29, John wrote:
Yes, you are correct. I've confirmed that we are using Artemis for the
subscriptions. ActiveMQ is being used for other purposes. The exception
from earlier was when using Artemis.

So to me, this means it's a bug. If the Client IDs are the same, an
exception is thrown when Artemis is used as the broker. Would the
developers be interested in looking into this? If so, how should we
submit
the issue to them?

Thanks
Sorry I was unclear earlier as I was in a rush and I misled you a bit.

A JMS client if setting a client ID must give a unique ID which is why
you got the error shown as your clients are all trying to connect with
the same client ID.  This is the issue though that prevents you from
using shared subscriptions in they way you want since if you set client
IDs the shared subscriptions cannot be shared across connections, only
from consumers created by sessions under the same connection.  Think of
the client ID as a namespace and the lack of one as a global namespace.
Shared subscriptions will work for you if your clients do not set a
client ID as you've seen because the namespace of all shared
subscriptions without a client ID is shared as a global namespace for
that subscription across connections.  Once you set a client ID you are
effectively in a private namespace owned by the connection that set that
ID and no two connections can share the same client ID.

ActiveMQ does not implement shared subscriptions at all so if you try to
use those APIs from the NMS client against that broker the result will
likely be an error response or something not doing what you want so you
need to use Apache Artemis if you want shared subscriptions but you will
need to stick to no client ID if you want to share across connections.
Likely you can accomplish what you want by simply moving to using a
Queue as consumers on a Queue all compete for messages (work sharing)
which seems like what you are going for. but your exact requirements
wasn't fully clear so you'd need to provide more info on why you want to
use shared subs.


On Tue, Mar 10, 2026 at 5:33 PM Timothy Bish <[email protected]>
wrote:
On 3/10/26 20:15, John wrote:
Thank you for your reply.

If I understand correctly, we should use the same client ID for each
client. And, if that is the case, then this has already been tried,
and the result is that every client after the first throws this
exception:
I know that ActiveMQ doesn't support shared subscriptions of any sort
so
if you tried this with ActiveMQ you will not get anything to work, and
if it did somehow create a subscription it would not be a valid one.
You would need to use Apache Artemis for shared subscription support as
that has full support for the AMQP JMS mapping.  As the specification
states the subscription is named by the subscription name and the
client
ID so if you want to share your clients either need no client ID or
they
all need the same for the subscription in question.


Unhandled exception. Apache.NMS.NMSException: , ErrorInfo = {
key: invalid-field, value: container-id;
}
    ---> Apache.NMS.AMQP.Util.NMSProviderError: amqp:invalid-field
      at Apache.NMS.AMQP.Util.ExceptionSupport.GetException(Error
amqpErr, String message, Exception e)
      at
Apache.NMS.AMQP.Provider.Amqp.AmqpConnection.OnClosed(IAmqpObject
sender, Error error)
      at Amqp.AmqpObject.NotifyClosed(Error error)
      at Amqp.Connection.OnEnded(Error error)
      at Amqp.Connection.OnClose(Close close)
      at Amqp.Connection.OnFrame(ByteBuffer buffer)
      at Amqp.AsyncPump.PumpAsync(UInt32 maxFrameSize, Func`2 onHeader,
Func`2 onBuffer)
      at
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.ExecutionContextCallback(Object
s)
      at System.Threading.ExecutionContext.RunInternal(ExecutionContext
executionContext, ContextCallback callback, Object state)
      at
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread
threadPoolThread)
      at
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext()
      at
System.Runtime.CompilerServices.TaskAwaiter.<>c.<OutputWaitEtwEvents>b__12_0(Action
innerContinuation, Task innerTask)
      at
System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(Action
action, Boolean allowInlining)
      at System.Threading.Tasks.Task.RunContinuations(Object
continuationObject)
      at System.Threading.Tasks.Task`1.TrySetResult(TResult result)
      at
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.SetExistingTaskResult(Task`1
task, TResult result)
      at Amqp.AsyncPump.ReceiveBufferAsync(Byte[] buffer, Int32 offset,
Int32 count)
      at
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.ExecutionContextCallback(Object
s)
      at System.Threading.ExecutionContext.RunInternal(ExecutionContext
executionContext, ContextCallback callback, Object state)
      at
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread
threadPoolThread)
      at
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext()
      at
System.Runtime.CompilerServices.TaskAwaiter.<>c.<OutputWaitEtwEvents>b__12_0(Action
innerContinuation, Task innerTask)
      at
System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(Action
action, Boolean allowInlining)
      at System.Threading.Tasks.Task.RunContinuations(Object
continuationObject)
      at System.Threading.Tasks.Task`1.TrySetResult(TResult result)
      at
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.SetExistingTaskResult(Task`1
task, TResult result)
      at
Amqp.TcpTransport.TcpSocket.Amqp.IAsyncTransport.ReceiveAsync(Byte[]
buffer, Int32 offset, Int32 count)
      at
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.ExecutionContextCallback(Object
s)
      at System.Threading.ExecutionContext.RunInternal(ExecutionContext
executionContext, ContextCallback callback, Object state)
      at
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread
threadPoolThread)
      at
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext()
      at
System.Runtime.CompilerServices.TaskAwaiter.<>c.<OutputWaitEtwEvents>b__12_0(Action
innerContinuation, Task innerTask)
      at
System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(Action
action, Boolean allowInlining)
      at System.Threading.Tasks.Task.RunContinuations(Object
continuationObject)
      at System.Threading.Tasks.Task`1.TrySetResult(TResult result)
      at
System.Threading.Tasks.TaskCompletionSource`1.TrySetResult(TResult
result)
      at Amqp.SocketExtensions.Complete[T](Object sender,
SocketAsyncEventArgs args, Boolean throwOnError, T result)
      at Amqp.TcpTransport.TcpSocket.<>c.<.ctor>b__6_1(Object s,
SocketAsyncEventArgs a)
      at System.Threading.ExecutionContext.RunInternal(ExecutionContext
executionContext, ContextCallback callback, Object state)
      at
System.Net.Sockets.SocketAsyncEventArgs.<>c.<.cctor>b__174_0(UInt32
errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
      at
System.Threading.PortableThreadPool.IOCompletionPoller.Event.Invoke()
      at
System.Threading.ThreadPoolTypedWorkItemQueue.System.Threading.IThreadPoolWorkItem.Execute()
      at System.Threading.ThreadPoolWorkQueue.Dispatch()
      at
System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()
      at System.Threading.Thread.StartCallback()

      --- End of inner exception stack trace ---
      at
Apache.NMS.AMQP.NmsConnection.CreateNmsConnectionInternal(Boolean
sync)
      at
Apache.NMS.AMQP.Util.Synchronization.TaskExtensions.AwaitRunContinuationAsync(Task
task)
      at Apache.NMS.AMQP.NmsConnection.StartAsync()
      at
Apache.NMS.AMQP.Util.Synchronization.TaskExtensions.GetAsyncResult(Task
task)
      at Apache.NMS.AMQP.NmsConnection.Start()




On Tue, Mar 10, 2026 at 4:48 PM Timothy Bish <[email protected]>
wrote:
On 3/10/26 19:42, John wrote:
Hi Tim,

Yes, each client has a unique Client ID. And this has been verified.
However, should the Client ID follow a specific pattern?
That's your problem then, you are creating many uniquely identified
shared subscriptions.

    From the JMS specification

"A shared non-durable subscription is identified by a name specified
by
the client and by the client
identifier if set. If the client identifier was set when the shared
non-durable subscription was first
created then a client which subsequently wishes to create a consumer
on
that shared non-durable
subscription must use the same client identifier."


On Tue, Mar 10, 2026 at 4:17 PM Timothy Bish <[email protected]>
wrote:
On 3/10/26 18:51, John wrote:
Hi,

I have stumbled across an issue that I believe is with
Apache.NMS.AMQP
library. I'm using the latest stable version, 2.4.0, for my .NET
C#
service
for message handling, with the latest Apache Artemis and Apache
ActiveMQ
message brokers.
Both the web page on Uri Configuration (

https://activemq.apache.org/components/nms/providers/amqp/uri-configuration
)
and the GitHub project (

https://github.com/apache/activemq-nms-amqp/blob/main/docs/configuration.md
)
state that "nms.clientId" should be used in the Connection Uri to
configure
the Client ID.
However, I have found that using "nms.clientId" breaks consumer
functionality for Shared Consumers, who will then receive every
message
in
the topic. And that, excluding the query parameter, allows Shared
Consumers
to function correctly, with each consumer receiving only their
share
of
the
messages.
Please also note that if one does not include the parameter in the
Uri
but
instead sets the ClientId on the connection object itself, the
Shared
Customer will still not function correctly, having the same issue
as
using
the parameter in the Uri.
However, by not including the nms.clientId parameter, a Client ID
is
automatically generated, which I guess is why it works correctly.
Nevertheless, it is less than ideal not being able to use my own
Client
ID.
But maybe I'm doing something wrong. Here is some simple example
code
using
the nms.clientId parameter that has the issue:

static async Task SharedAsync(string clientId, CancellationToken
stoppingToken)
{
         string uri =

$"amqp://localhost:61616?nms.username=artemis&nms.password=artemis&nms.clientId={clientId}";
Are you giving each client the same ID or are they each being
assigned
there own unique client ID ?


         var connecturi = new Uri(uri);
         var factory = new NMSConnectionFactory(connecturi);
         using IConnection connection = await
factory.CreateConnectionAsync();
         connection.Start();
         using ISession session = await
connection.CreateSessionAsync();
         ITopic destination = await
session.GetTopicAsync("topic-name");
         using var consumer = await
session.CreateSharedConsumerAsync(destination, "sub-name");
         while (!stoppingToken.IsCancellationRequested)
         {
             var msg = await
consumer.ReceiveAsync(TimeSpan.FromMicroseconds(250)) as
ITextMessage;
             if (msg is not null)
             {
                 // process message
             }
         }
}


Thanks,
John

--
Tim Bish



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information, visit:
https://activemq.apache.org/contact


--
Tim Bish


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information, visit: https://activemq.apache.org/contact


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information, visit: https://activemq.apache.org/contact


--
Tim Bish


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information, visit: https://activemq.apache.org/contact



--
Tim Bish


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information, visit: https://activemq.apache.org/contact




--
Tim Bish


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information, visit: https://activemq.apache.org/contact


Reply via email to