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

AzureServiceBus: Local transactions cannot span multiple top-level entities such as queue or topic #137

Open
adelinor opened this issue Jul 6, 2022 · 6 comments

Comments

@adelinor
Copy link

adelinor commented Jul 6, 2022

Specifications

  • JMSToolBox version: 5.14.0
  • OS (Name, version, 32/64bits): Windows 10 Enterprise 64 bits
  • Q provider (kind, version) : AzureServiceBus

Expected Behavior

As for other Q providers, to be able to view the content of more than one queue within an open session

Actual Behavior

When trying to open a second queue, the following error message is shown:

org.apache.qpid.jms.provider.ProviderException: Local transactions cannot span multiple top-level entities such as queue or topic. TrackingId:b328e537ae6e4dafb967a1e1883165fd_G10S2, SystemTracker:gateway5, Timestamp:2022-07-06T06:45:41 [condition = amqp:not-allowed]
Local transactions cannot span multiple top-level entities such as queue or topic. TrackingId:b328e537ae6e4dafb967a1e1883165fd_G10S2, SystemTracker:gateway5, Timestamp:2022-07-06T06:45:41 [condition = amqp:not-allowed]

Steps to Reproduce the Problem

  1. Setup a premium Azure Service Bus resource with at least two queues (the premium offering provides JMS 2.0 support)
  2. Get hold of a valid Azure Service Connection string with Manage, Listen, and Send rights
  3. Configure a session in JMSToolBox for Azure Service Bus by providing a dummy host and port, and provide the connection string in Properties/connectionString
  4. Open the configured session. You should see at least two queues.
  5. Open one queue and open a second one

Logs

"<user>\.jtb\JMSToolBox\jmstoolbox.log"
jmstoolbox.log

"<user>\.jtb\.metadata\.log"
.log

@titou10titou10
Copy link
Contributor

I don't know anything about AzureSB but it should work. "Cross entity transactions" is supported if Connection.createSession(true, Session.SESSION_TRANSACTED) is used (JMSToolBox does that...):
https://docs.microsoft.com/en-us/azure/service-bus-messaging/how-to-use-java-message-service-20#what-jms-features-are-supported

Q:

  • Do you see the same behaviour with all pairs of Queues?
  • Do you have another JMS client that works with those 2 queues?

I upgraded the version of qpid and other dependent jars used by the azure client just in case...but I don't think it will change anything

@adelinor
Copy link
Author

adelinor commented Jul 7, 2022

Dear @titou10titou10 ,

I can confirm that the error occurs with any queue and in any order. You can open one queue and any attempt to open a different queue will prompt the error.

Apart from the applications that I develop the only JMS client I use is JMSToolBox :) .

If that is of any help, using the same stack used in the apps, I wrote a unit test that browses several queues without errors from AzureServiceBus can be browsed.

package samples.azsbuscalculator.browser;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.QueueBrowser;
import javax.jms.Session;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jms.core.JmsTemplate;

@SpringBootTest
class BrowseQueueTest {
    
    @Autowired
    private JmsTemplate template;

    @Test
    void testBrowseQueues() {
        List<String> names = Arrays.asList(
                "calculator/inbox",
                "calculator/outbox",
                "calculator/inbox/$deadletterqueue",
                "calculator/outbox/$deadletterqueue");
        for (String queueName : names) {
            List<String> details = this.template.browse(queueName,
                    SendMessageTest::browseQueue);
            System.out.println(queueName + ": " + details.size() + " messages");
            if (! details.isEmpty()) {
                for (String summary : details) {
                    System.out.println("    " + summary);
                }            
            }            
        }
    }
    
    private static List<String> browseQueue(Session session, QueueBrowser browser) throws JMSException {
        List<String> result = new ArrayList<>();
        Enumeration enumeration = browser.getEnumeration();
        while (enumeration.hasMoreElements()) {
            Message message = (Message) enumeration.nextElement();
            Date jmsTimestamp = new Date(message.getJMSTimestamp());
            result.add(jmsTimestamp + " - " + message.getJMSMessageID());
        }
        return result;
    }
}

The project containing this test was generated with Spring Initialzr:

  • Spring boot version = 2.6.9
  • Java version = 8

After generating the project, the dependency for service bus is added as recommended in the article https://docs.microsoft.com/en-us/azure/developer/java/spring-framework/configure-spring-boot-starter-java-app-with-azure-service-bus

<!-- AZURE SERVICE BUS -->
<dependency>
    <groupId>com.azure.spring</groupId>
    <artifactId>spring-cloud-azure-starter-servicebus-jms</artifactId>
    <version>4.3.0</version>
</dependency>

Configuration is done as per the article with properties:

spring.jms.servicebus.connection-string=...
spring.jms.servicebus.idle-timeout=120000
spring.jms.servicebus.pricing-tier=premium

Kind regards

@titou10titou10
Copy link
Contributor

I guess that the code above just open a JMSSession for browsing (I don't know anything about Spring and what magic goes underneath)
I don't know also how Spring sets the transaction boundaries: One per class/test? one per method? ...

JTB open a JMSSession also to manage local transactions, because it is also used to delete/put etc. messages, not only browsing :

jmsSession = jmsConnection.createSession(true, Session.SESSION_TRANSACTED);

@adelinor
Copy link
Author

adelinor commented Jul 8, 2022

Running the test above creates a single session with the method

org.apache.qpid.jms.JmsConnection.createSession(boolean, int)

with following argument values:

  • transacted = false
  • acknowledgeMode = 1

@adelinor
Copy link
Author

Hi @titou10titou10 ,

It seems that Azure Service Bus imposes restrictions when used in a transacted context: see comment Azure/azure-sdk-for-java#29881 (comment) .

As a conclusion this issue is not resolvable in the jmstoolbox :(

Kind regards

@titou10titou10
Copy link
Contributor

Thanks for your feed back. This is a serious limitation of the Azure SB SDK...

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

No branches or pull requests

2 participants