Skip to content

Commit f7b4dcf

Browse files
committed
Fix ActivityCorrelator behavior
1 parent 1f69f48 commit f7b4dcf

File tree

2 files changed

+98
-28
lines changed

2 files changed

+98
-28
lines changed

src/main/java/com/microsoft/sqlserver/jdbc/ActivityCorrelator.java

+31-28
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,28 @@
66
package com.microsoft.sqlserver.jdbc;
77

88
import java.util.UUID;
9-
import java.util.concurrent.locks.Lock;
10-
import java.util.concurrent.locks.ReentrantLock;
119

1210

1311
/**
1412
* ActivityCorrelator provides the APIs to access the ActivityId in TLS
1513
*/
1614
final class ActivityCorrelator {
1715

18-
private static ActivityId activityId;
19-
private static Lock lockObject = new ReentrantLock();
20-
21-
// Get the current ActivityId in TLS
22-
static ActivityId getCurrent() {
23-
if (activityId == null) {
24-
lockObject.lock();
25-
if (activityId == null) {
26-
activityId = new ActivityId();
27-
}
28-
lockObject.unlock();
16+
private static ThreadLocal<ActivityId> t_ActivityId = new ThreadLocal<ActivityId>() {
17+
@Override
18+
protected ActivityId initialValue() {
19+
return new ActivityId();
2920
}
21+
};
3022

31-
return activityId;
23+
static ActivityId getCurrent() {
24+
return t_ActivityId.get();
3225
}
3326

3427
// Increment the Sequence number of the ActivityId in TLS
3528
// and return the ActivityId with new Sequence number
3629
static ActivityId getNext() {
37-
// Get the current ActivityId in TLS
38-
ActivityId activityId = getCurrent();
39-
40-
// Increment the Sequence number
41-
activityId.increment();
42-
43-
return activityId;
30+
return getCurrent().getIncrement();
4431
}
4532

4633
/*
@@ -53,35 +40,51 @@ private ActivityCorrelator() {}
5340
class ActivityId {
5441
private final UUID id;
5542
private long sequence;
43+
// Cache the string since it gets frequently referenced.
44+
private String cachedToString;
5645

5746
ActivityId() {
5847
id = UUID.randomUUID();
59-
sequence = 1;
48+
// getNext() is called during prelogin and will be the logical "first call" after
49+
// instantiation, incrementing this to >= 1 before any activity logs are written.
50+
sequence = 0;
6051
}
6152

6253
UUID getId() {
6354
return id;
6455
}
6556

6657
long getSequence() {
58+
// Edge case: A new thread re-uses an existing connection. Ensure sequence > 0.
59+
if (sequence == 0L) {
60+
++sequence;
61+
}
62+
6763
return sequence;
6864
}
6965

70-
void increment() {
66+
ActivityId getIncrement() {
67+
cachedToString = null;
7168
if (sequence < 0xffffffffl) // to get to 32-bit unsigned
7269
{
7370
++sequence;
7471
} else {
7572
sequence = 0;
7673
}
74+
75+
return this;
7776
}
7877

7978
@Override
8079
public String toString() {
81-
StringBuilder sb = new StringBuilder();
82-
sb.append(id.toString());
83-
sb.append("-");
84-
sb.append(sequence);
85-
return sb.toString();
80+
if (cachedToString == null) {
81+
StringBuilder sb = new StringBuilder(38);
82+
sb.append(id.toString());
83+
sb.append("-");
84+
sb.append(getSequence());
85+
cachedToString = sb.toString();
86+
}
87+
88+
return cachedToString.toString();
8689
}
8790
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Microsoft JDBC Driver for SQL Server Copyright(c) Microsoft Corporation All rights reserved. This program is made
3+
* available under the terms of the MIT License. See the LICENSE file in the project root for more information.
4+
*/
5+
package com.microsoft.sqlserver.jdbc;
6+
7+
import static org.junit.Assert.assertFalse;
8+
import static org.junit.jupiter.api.Assertions.assertEquals;
9+
10+
import java.util.ArrayList;
11+
import java.util.List;
12+
import java.util.UUID;
13+
14+
import org.junit.jupiter.api.Test;
15+
import org.junit.platform.runner.JUnitPlatform;
16+
import org.junit.runner.RunWith;
17+
18+
import com.microsoft.sqlserver.testframework.AbstractTest;
19+
20+
21+
@RunWith(JUnitPlatform.class)
22+
public class ActivityIDTest extends AbstractTest {
23+
24+
@Test
25+
public void testActivityID() throws Exception {
26+
int numThreads = 20;
27+
List<UUID> usedIds = new ArrayList<UUID>(numThreads);
28+
ActivityId id = ActivityCorrelator.getCurrent();
29+
usedIds.add(id.getId());
30+
assertEquals(1L, id.getSequence());
31+
ActivityCorrelator.getNext();
32+
assertEquals(2L, id.getSequence());
33+
ActivityCorrelator.getNext();
34+
assertEquals(3L, id.getSequence());
35+
36+
for (int i = 0; i < numThreads; i++) {
37+
MyThread t = new MyThread(usedIds);
38+
t.start();
39+
t.join();
40+
usedIds.add(t.getUUID());
41+
}
42+
}
43+
44+
public class MyThread extends Thread {
45+
46+
public MyThread(List<UUID> usedIdsIn) {
47+
usedIds = usedIdsIn;
48+
}
49+
50+
private List<UUID> usedIds;
51+
private ActivityId id;
52+
53+
public UUID getUUID() {
54+
return id.getId();
55+
}
56+
57+
public void run() {
58+
id = ActivityCorrelator.getCurrent();
59+
assertFalse(usedIds.contains(id.getId()));
60+
assertEquals(1L, id.getSequence());
61+
id = ActivityCorrelator.getNext();
62+
assertEquals(2L, id.getSequence());
63+
id = ActivityCorrelator.getNext();
64+
assertEquals(3L, id.getSequence());
65+
}
66+
}
67+
}

0 commit comments

Comments
 (0)