This is an automated email from the ASF dual-hosted git repository.
damccorm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git
The following commit(s) were added to refs/heads/master by this push:
new 6e21dba8c6d Deflake JmsIOTest checkpoint and authentication tests
(#40291)
6e21dba8c6d is described below
commit 6e21dba8c6d5f42a50fb6ef9c2af5c0b98519f36
Author: Maksym Tymoshyk <[email protected]>
AuthorDate: Fri Oct 9 16:33:32 2026 +0300
Deflake JmsIOTest checkpoint and authentication tests (#40291)
- The slow-ack proxy wrapped every receiveNoWait() result, including null,
so testCheckpointMarkSafety failed with an NPE when qpid returned no
message
on a slow runner. Only wrap non-null results.
- With prefetch disabled, reader.start() can return false the same way
advance() can. Add startWithRetry(), mirroring advanceWithRetry().
- ActiveMQ stops the connection shortly after an authentication failure.
With
the async vm:// transport that stop can overtake the error reply, so the
auth tests saw "peer ... stopped." instead of the auth error. Use a
synchronous vm:// transport for those tests.
Fixes #30225
Fixes #40289
---
.../java/org/apache/beam/sdk/io/jms/JmsIOTest.java | 37 ++++++++++++++++------
1 file changed, 27 insertions(+), 10 deletions(-)
diff --git
a/sdks/java/io/jms/src/test/java/org/apache/beam/sdk/io/jms/JmsIOTest.java
b/sdks/java/io/jms/src/test/java/org/apache/beam/sdk/io/jms/JmsIOTest.java
index c92794c21e5..bdcf27bbfa4 100644
--- a/sdks/java/io/jms/src/test/java/org/apache/beam/sdk/io/jms/JmsIOTest.java
+++ b/sdks/java/io/jms/src/test/java/org/apache/beam/sdk/io/jms/JmsIOTest.java
@@ -190,10 +190,23 @@ public class JmsIOTest {
}
}
+ /**
+ * ActiveMQ stops the connection shortly after an authentication failure.
With the default async
+ * vm:// transport, that stop can overtake the error reply on a slow
machine, and the client sees
+ * "peer ... stopped." instead of the authentication error. A synchronous
transport delivers the
+ * reply first.
+ */
+ private ConnectionFactory connectionFactoryForAuthFailure() {
+ return connectionFactoryClass == ActiveMQConnectionFactory.class
+ ? new ActiveMQConnectionFactory(String.format("%s:%d?async=false",
brokerUrl, brokerPort))
+ : connectionFactory;
+ }
+
@Test
public void testAuthenticationRequired() {
// A topic produces one source, avoiding races between concurrent
authentication failures.
-
pipeline.apply(JmsIO.read().withConnectionFactory(connectionFactory).withTopic(TOPIC));
+ pipeline.apply(
+
JmsIO.read().withConnectionFactory(connectionFactoryForAuthFailure()).withTopic(TOPIC));
String errorMessage =
this.connectionFactoryClass == ActiveMQConnectionFactory.class
? "User name [null] or password is invalid."
@@ -205,7 +218,7 @@ public class JmsIOTest {
public void testAuthenticationWithBadPassword() {
pipeline.apply(
JmsIO.read()
- .withConnectionFactory(connectionFactory)
+ .withConnectionFactory(connectionFactoryForAuthFailure())
.withTopic(TOPIC)
.withUsername(USERNAME)
.withPassword("BAD"));
@@ -361,7 +374,7 @@ public class JmsIOTest {
JmsIO.UnboundedJmsReader reader = source.createReader(null, null);
// start the reader and move to the first record
- assertTrue(reader.start());
+ assertTrue(startWithRetry(reader));
Set<ByteBuffer> uniqueIds = new HashSet<>();
uniqueIds.add(ByteBuffer.wrap(reader.getCurrentRecordId()));
@@ -494,6 +507,10 @@ public class JmsIOTest {
assertEquals(100, count);
}
+ private boolean startWithRetry(UnboundedSource.UnboundedReader reader)
throws IOException {
+ return reader.start() || advanceWithRetry(reader);
+ }
+
private boolean advanceWithRetry(UnboundedSource.UnboundedReader reader)
throws IOException {
for (int attempt = 0; attempt < 10; attempt++) {
if (reader.advance()) {
@@ -517,7 +534,7 @@ public class JmsIOTest {
UnboundedJmsReader reader = setupReaderForTest();
// start the reader and move to the first record
- assertTrue(reader.start());
+ assertTrue(startWithRetry(reader));
// consume 3 messages (NB: start already consumed the first message)
for (int i = 0; i < 3; i++) {
@@ -552,7 +569,7 @@ public class JmsIOTest {
UnboundedJmsReader reader = setupReaderForTest();
// start the reader and move to the first record
- assertTrue(reader.start());
+ assertTrue(startWithRetry(reader));
// consume 2 message (NB: start already consumed the first message)
assertTrue(advanceWithRetry(reader));
@@ -585,7 +602,7 @@ public class JmsIOTest {
public void testCheckpointMarkAndFinalizeSeparatelyIndividualAcknowledge()
throws Exception {
UnboundedJmsReader reader =
setupReaderForTest(JmsIO.AcknowledgeMode.INDIVIDUAL_ACKNOWLEDGE);
- assertTrue(reader.start());
+ assertTrue(startWithRetry(reader));
assertTrue(advanceWithRetry(reader));
assertTrue(advanceWithRetry(reader));
@@ -610,7 +627,7 @@ public class JmsIOTest {
public void testCheckpointMarkAndFinalizeSeparatelyClientAcknowledgeUnsafe()
throws Exception {
UnboundedJmsReader reader =
setupReaderForTest(JmsIO.AcknowledgeMode.CLIENT_ACKNOWLEDGE_UNSAFE);
- assertTrue(reader.start());
+ assertTrue(startWithRetry(reader));
assertTrue(advanceWithRetry(reader));
assertTrue(advanceWithRetry(reader));
@@ -747,7 +764,7 @@ public class JmsIOTest {
JmsIO.UnboundedJmsReader reader =
source.createReader(PipelineOptionsFactory.create(), null);
// start the reader and move to the first record
- assertTrue(reader.start());
+ assertTrue(startWithRetry(reader));
// consume half the messages (NB: start already consumed the first message)
for (int i = 0; i < (messagesToProcess / 2) - 1; i++) {
@@ -847,7 +864,7 @@ public class JmsIOTest {
JmsIO.UnboundedJmsReader reader =
source.createReader(PipelineOptionsFactory.create(), null);
// start the reader and move to the first record
- assertTrue(reader.start());
+ assertTrue(startWithRetry(reader));
// consume 3 more messages (NB: start already consumed the first message)
for (int i = 0; i < 3; i++) {
@@ -1116,7 +1133,7 @@ public class JmsIOTest {
new Class[] {proxyInterface},
(proxy, method, args) -> {
Object result = method.invoke(target, args);
- if (method.getName().equals(methodName)) {
+ if (result != null && method.getName().equals(methodName)) {
result = resultTransformer.apply((MethodArgT) result);
}
return result;