Skip to content

Commit 1faebac

Browse files
author
Alexandre Dutra
committed
JAVA-1392: Reduce lock contention in RPTokenFactory
Motivation: RPTokenFactory.md5() is called for almost every query plan by TokenAwarePolicy. This method in turn calls MessageDigest.getInstance(String), which is known to create lock contentions (see https://bugs.openjdk.java.net/browse/JDK-7092821). Modification: Clone a prototype instance of MessageDigest instead of creating a new one from scratch, thus avoiding lock contention. See google/guava#1197. Result: RPTokenFactory.md5() has an improved performance as no more lock contentions arrive.
1 parent 00c692f commit 1faebac

3 files changed

Lines changed: 46 additions & 5 deletions

File tree

changelog/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- [improvement] JAVA-1308: CodecRegistry performance improvements.
1111
- [improvement] JAVA-1241: Upgrade Netty to 4.1.x.
1212
- [improvement] JAVA-1287: Add CDC to TableOptionsMetadata and Schema Builder.
13+
- [improvement] JAVA-1392: Reduce lock contention in RPTokenFactory.
1314

1415
Merged from 3.1.x branch:
1516

driver-core/src/main/java/com/datastax/driver/core/Token.java

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,6 @@ private long murmur(ByteBuffer data) {
230230
k1 *= c2;
231231
h1 ^= k1;
232232
}
233-
;
234233

235234
//----------
236235
// finalization
@@ -569,16 +568,45 @@ private static class RPTokenFactory extends Factory {
569568
private static final Token MIN_TOKEN = new RPToken(MIN_VALUE);
570569
private static final Token MAX_TOKEN = new RPToken(MAX_VALUE);
571570

572-
private BigInteger md5(ByteBuffer data) {
571+
private final MessageDigest prototype;
572+
private final boolean supportsClone;
573+
574+
private RPTokenFactory() {
575+
prototype = createMessageDigest();
576+
boolean supportsClone;
573577
try {
574-
MessageDigest digest = MessageDigest.getInstance("MD5");
575-
digest.update(data.duplicate());
576-
return new BigInteger(digest.digest()).abs();
578+
prototype.clone();
579+
supportsClone = true;
580+
} catch (CloneNotSupportedException e) {
581+
supportsClone = false;
582+
}
583+
this.supportsClone = supportsClone;
584+
}
585+
586+
private static MessageDigest createMessageDigest() {
587+
try {
588+
return MessageDigest.getInstance("MD5");
577589
} catch (NoSuchAlgorithmException e) {
578590
throw new RuntimeException("MD5 doesn't seem to be available on this JVM", e);
579591
}
580592
}
581593

594+
private MessageDigest newMessageDigest() {
595+
if (supportsClone) {
596+
try {
597+
return (MessageDigest) prototype.clone();
598+
} catch (CloneNotSupportedException ignored) {
599+
}
600+
}
601+
return createMessageDigest();
602+
}
603+
604+
private BigInteger md5(ByteBuffer data) {
605+
MessageDigest digest = newMessageDigest();
606+
digest.update(data.duplicate());
607+
return new BigInteger(digest.digest()).abs();
608+
}
609+
582610
@Override
583611
RPToken fromString(String tokenStr) {
584612
return new RPToken(new BigInteger(tokenStr));

driver-core/src/test/java/com/datastax/driver/core/RPTokenFactoryTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,27 @@
1515
*/
1616
package com.datastax.driver.core;
1717

18+
import com.datastax.driver.core.utils.Bytes;
1819
import org.testng.annotations.Test;
1920

21+
import java.nio.ByteBuffer;
2022
import java.util.List;
2123

2224
import static org.assertj.core.api.Assertions.assertThat;
2325

2426
public class RPTokenFactoryTest {
2527
Token.Factory factory = Token.RPToken.FACTORY;
2628

29+
@Test(groups = "unit")
30+
public void should_hash_consistently() {
31+
ByteBuffer byteBuffer = Bytes.fromHexString("0xCAFEBABE");
32+
Token tokenA = factory.hash(byteBuffer);
33+
Token tokenB = factory.hash(byteBuffer);
34+
assertThat(tokenA)
35+
.isEqualTo(factory.fromString("59959303159920881837560881824507314222"))
36+
.isEqualTo(tokenB);
37+
}
38+
2739
@Test(groups = "unit")
2840
public void should_split_range() {
2941
List<Token> splits = factory.split(factory.fromString("0"), factory.fromString("127605887595351923798765477786913079296"), 3);

0 commit comments

Comments
 (0)