Skip to content

Commit

Permalink
Merge pull request #40613 from marcosgopen/node-name
Browse files Browse the repository at this point in the history
Ensure the Transaction Manager node name is less than or equal to 28 bytes
  • Loading branch information
yrodiere authored Oct 22, 2024
2 parents 242d9aa + e14067b commit 4a56283
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 2 deletions.
7 changes: 7 additions & 0 deletions docs/src/main/asciidoc/transaction.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,13 @@ 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 cannot be longer than 28 bytes.
To automatically shorten names longer than 28 bytes, set `quarkus.transaction-manager.shorten-node-name-if-necessary` to `true`.
Shortening is implemented by hashing the node name, encoding the hash to Base64 and then truncating the result. As with all hashes, the resulting shortened node name could potentially conflict with another shortened node name, but it is https://github.com/quarkusio/quarkus/issues/30491#issuecomment-1537247764[very unlikely].
====

[[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 shortened to 28 bytes.
*
* @see #nodeName
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package io.quarkus.narayana.jta.runtime;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.nio.charset.StandardCharsets;

import org.junit.jupiter.api.Test;

public class NarayanaJtaRecorderTest {

//this string has been chosen as when hashed and Base64 encoded the resulted byte array will have a length > 28, so it will be trimmed too.
public static final String NODE_NAME_TO_SHORTEN = "dfe2420d-b12e-4ec3-92c0-ee7c4";

@Test
void testByteLengthWithLongerString() {
TransactionManagerConfiguration transactions = new TransactionManagerConfiguration();
transactions.shortenNodeNameIfNecessary = true;
// create nodeNames larger than 28 bytes
assertTrue(NODE_NAME_TO_SHORTEN.getBytes(StandardCharsets.UTF_8).length > 28);
NarayanaJtaRecorder r = new NarayanaJtaRecorder();
transactions.nodeName = NODE_NAME_TO_SHORTEN;
r.setNodeName(transactions);
int numberOfBytes = transactions.nodeName.getBytes(StandardCharsets.UTF_8).length;
assertEquals(28, numberOfBytes,
"node name bytes was not 28 bytes limit, number of bytes is " + numberOfBytes);
}

@Test
void testPredictableConversion() {
TransactionManagerConfiguration transactions = new TransactionManagerConfiguration();
transactions.shortenNodeNameIfNecessary = true;
assertTrue(NODE_NAME_TO_SHORTEN.getBytes(StandardCharsets.UTF_8).length > 28);
NarayanaJtaRecorder r = new NarayanaJtaRecorder();
transactions.nodeName = NODE_NAME_TO_SHORTEN;
r.setNodeName(transactions);
int numberOfBytes = transactions.nodeName.getBytes(StandardCharsets.UTF_8).length;
assertEquals(28, numberOfBytes,
"node name bytes was not 28 bytes limit, number of bytes is " + numberOfBytes);
String firstConversion = transactions.nodeName;
transactions.nodeName = NODE_NAME_TO_SHORTEN;
r.setNodeName(transactions);
String secondConversion = transactions.nodeName;
numberOfBytes = transactions.nodeName.getBytes(StandardCharsets.UTF_8).length;
assertEquals(28, numberOfBytes,
"node name bytes was not 28 bytes limit, number of bytes is " + numberOfBytes);
assertEquals(firstConversion, secondConversion,
"Node names were shortened differently: " + firstConversion + " " + secondConversion);
}
}

0 comments on commit 4a56283

Please sign in to comment.