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
Add unit test, stress test commented
  • Loading branch information
marcosgopen committed May 14, 2024
1 parent eb49de2 commit 8730b57
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 2 deletions.
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 @@ -57,9 +58,19 @@ private static void shortenNodeName(TransactionManagerConfiguration transactions
String originalNodeName = transactions.nodeName;
log.warnf("Node name \"%s\" is longer than 28 bytes, shortening it by using %s.", originalNodeName,
HASH_ALGORITHM_FOR_SHORTENING);
final byte[] nodeNameAsBytes = originalNodeName.getBytes();
final byte[] nodeNameAsBytes = originalNodeName.getBytes(StandardCharsets.UTF_8);
MessageDigest messageDigest224 = MessageDigest.getInstance(HASH_ALGORITHM_FOR_SHORTENING);
transactions.nodeName = new String(messageDigest224.digest(nodeNameAsBytes), StandardCharsets.UTF_8);
byte[] hashedByteArray = messageDigest224.digest(nodeNameAsBytes);

//Each Base64 digit represents 6 bits of data
//encoding the array might result in a longer array
//this is needed to guarantee that the resulting string has a deterministic and limited length,
//and the conversion is reversible
byte[] base64Result = Base64.getEncoder().encode(hashedByteArray);
//truncate the array
byte[] slice = Arrays.copyOfRange(base64Result, 0, 27);

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
@@ -0,0 +1,62 @@
package io.quarkus.narayana.jta.runtime;

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

import java.nio.charset.StandardCharsets;
import java.util.SortedSet;
import java.util.TreeSet;
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;
if (numberOfBytes > 28) {
fail("node name bytes still exceeded 28 bytes limit, number of bytes is " + numberOfBytes);
}
}
// for (int i = 1; i < 20000; i++) {
for (int i = 1; i < 2000; 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;
if (numberOfBytes > 28) {
fail("node name bytes still exceeded 28 bytes limit, number of bytes is " + numberOfBytes);
}
}
}

//commented because it is a stress test
// @Test
void testCollision() {
SortedSet<String> set = new TreeSet<>();
TransactionManagerConfiguration transactions = new TransactionManagerConfiguration();
transactions.shortenNodeNameIfNecessary = true;
// create a nodeName larger than 28 bytes
for (int i = 1; i < 1000000; i++) {
String originalNodeName = UUID.randomUUID().toString();
NarayanaJtaRecorder r = new NarayanaJtaRecorder();
transactions.nodeName = originalNodeName;
r.setNodeName(transactions);
if (set.contains(transactions.nodeName)) {
fail("Collision of IDs for " + transactions.nodeName);
}
set.add(transactions.nodeName);
}
}

}

0 comments on commit 8730b57

Please sign in to comment.