Skip to content

Commit

Permalink
node-name needs to be truncated because the byte array is not base64
Browse files Browse the repository at this point in the history
  • Loading branch information
marcosgopen committed Oct 17, 2024
1 parent 7097ad7 commit 44d9f80
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 2 deletions.
12 changes: 12 additions & 0 deletions docs/src/main/asciidoc/transaction.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,18 @@ The node name identifier needs to be unique per transaction manager deployment.
And the node identifier needs to be stable over the transaction manager restarts.

The node name identifier may be configured via the property `quarkus.transaction-manager.node-name`.
[NOTE]
====
The node-name property has a length limitation, it cannot be longer than 28 bytes.
The length limitation can possibly be solved by enabling the property 'quarkus.transaction-manager.shortenNodeNameIfNecessary' which, when enabled, will shorten names longer than 28 bytes.
The shortening process is done by applying the 'SHA-224' algorithm, encoding to Base64 and then truncating the result. Therefore this may result in the node-name ending as an unpredictable duplicate string and this breaks the uniqueness that is mandatory for safe transaction usage.
For more information about the potential for conflict see https://github.com/quarkusio/quarkus/issues/30491
====



Highlight it as a caution that this may result in the node-name ending as an unpredictable duplicate string and this break the uniqueness that is mandatory for safe transaction usage. Perhaps the comment could also mention: #30491 which has some rationale to try to help guide on the potential for conflict.

[[transaction-scope]]
== Using `@TransactionScoped` to bind CDI beans to the transaction lifecycle
Expand Down
11 changes: 11 additions & 0 deletions extensions/narayana-jta/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@
<groupId>org.jboss.narayana.jts</groupId>
<artifactId>narayana-jts-integration</artifactId>
</dependency>
<!-- Test Dependencies -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-internal</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -60,7 +61,15 @@ private static void shortenNodeName(TransactionManagerConfiguration transactions
HASH_ALGORITHM_FOR_SHORTENING);
final byte[] nodeNameAsBytes = originalNodeName.getBytes();
MessageDigest messageDigest224 = MessageDigest.getInstance(HASH_ALGORITHM_FOR_SHORTENING);
transactions.nodeName = new String(messageDigest224.digest(nodeNameAsBytes), StandardCharsets.UTF_8);
byte[] hashedByteArray = messageDigest224.digest(nodeNameAsBytes);

//Encode the byte array in Base64
//encoding the array might result in a longer array
byte[] base64Result = Base64.getEncoder().encode(hashedByteArray);
//truncate the array
byte[] slice = Arrays.copyOfRange(base64Result, 0, 28);

transactions.nodeName = new String(slice, StandardCharsets.UTF_8);
log.warnf("New node name is \"%s\"", transactions.nodeName);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public final class TransactionManagerConfiguration {
* Whether the node name should be shortened if necessary.
* The node name must not exceed a length of 28 bytes. If this property is set to {@code true}, and the node name exceeds 28
* bytes, the node name is shortened by calculating the <a href="https://en.wikipedia.org/wiki/SHA-2">SHA-224</a> hash,
* which has a length of 28 bytes.
* which has a length of 28 bytes, encoded to Base64 format and then shorten to 28 bytes.
*
* @see #nodeName
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.quarkus.narayana.jta.runtime;

import static org.junit.jupiter.api.Assertions.assertFalse;

import java.nio.charset.StandardCharsets;
import java.util.UUID;

import org.junit.jupiter.api.Test;

public class NarayanaJtaRecorderTest {

@Test
void testByteLength() {
TransactionManagerConfiguration transactions = new TransactionManagerConfiguration();
transactions.shortenNodeNameIfNecessary = true;
// create nodeNames larger than 28 bytes
for (int i = 1; i < 100; i++) {
String originalNodeName = UUID.randomUUID().toString();
NarayanaJtaRecorder r = new NarayanaJtaRecorder();
transactions.nodeName = originalNodeName;
r.setNodeName(transactions);
int numberOfBytes = transactions.nodeName.getBytes(StandardCharsets.UTF_8).length;
assertFalse(numberOfBytes > 28,
"node name bytes still exceeded 28 bytes limit, number of bytes is " + numberOfBytes);
}
for (int i = 1; i < 1000; i++) {
byte[] data = new byte[i];
NarayanaJtaRecorder r = new NarayanaJtaRecorder();
transactions.nodeName = new String(data);
r.setNodeName(transactions);
int numberOfBytes = transactions.nodeName.getBytes(StandardCharsets.UTF_8).length;
assertFalse(numberOfBytes > 28,
"node name bytes still exceeded 28 bytes limit, number of bytes is " + numberOfBytes);
}
}

}

0 comments on commit 44d9f80

Please sign in to comment.