Skip to content

Commit 4f732e9

Browse files
authored
Remove remaining threadlocals for loom support (#3286)
* Replaced signature parser ThreadLocal with pool * Replaced transactions metrics labels ThreadLocal with transaction field * Removed OkHttp cached StringBuilder * updated changelog * Remove unnecessary setLength call
1 parent 796c694 commit 4f732e9

File tree

4 files changed

+29
-60
lines changed

4 files changed

+29
-60
lines changed

CHANGELOG.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Use subheadings with the "=====" level for adding notes for unreleased changes:
3333
3434
[float]
3535
===== Features
36-
* Virtual thread support - {pull}3244[#3244]
36+
* Virtual thread support - {pull}3244[#3244], {pull}3286[#3286]
3737
3838
[float]
3939
===== Bug fixes

apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/Transaction.java

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,11 @@
4949
*/
5050
public class Transaction extends AbstractSpan<Transaction> implements co.elastic.apm.agent.tracer.Transaction<Transaction> {
5151

52-
private static final ThreadLocal<Labels.Mutable> labelsThreadLocal = new ThreadLocal<Labels.Mutable>() {
53-
@Override
54-
protected Labels.Mutable initialValue() {
55-
return Labels.Mutable.of();
56-
}
57-
};
52+
/**
53+
* Mutable labels instance used when reporting transaction metrics.
54+
* This is a field to prevent allocations.
55+
*/
56+
private final Labels.Mutable labelsMutable = Labels.Mutable.of();
5857

5958
/**
6059
* Context
@@ -507,9 +506,8 @@ private void trackMetrics() {
507506
if (type == null) {
508507
return;
509508
}
510-
final Labels.Mutable labels = labelsThreadLocal.get();
511-
labels.resetState();
512-
labels.serviceName(getTraceContext().getServiceName())
509+
labelsMutable.resetState();
510+
labelsMutable.serviceName(getTraceContext().getServiceName())
513511
.serviceVersion(getTraceContext().getServiceVersion())
514512
.transactionName(name)
515513
.transactionType(type);
@@ -529,8 +527,8 @@ private void trackMetrics() {
529527
if (subtype.equals("")) {
530528
subtype = null;
531529
}
532-
labels.spanType(spanType).spanSubType(subtype);
533-
metricRegistry.updateTimer("span.self_time", labels, timer.getTotalTimeUs(), timer.getCount());
530+
labelsMutable.spanType(spanType).spanSubType(subtype);
531+
metricRegistry.updateTimer("span.self_time", labelsMutable, timer.getTotalTimeUs(), timer.getCount());
534532
timer.resetState();
535533
}
536534
}

apm-agent-plugin-sdk/src/main/java/co/elastic/apm/agent/sdk/internal/db/signature/SignatureParser.java

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818
*/
1919
package co.elastic.apm.agent.sdk.internal.db.signature;
2020

21-
import co.elastic.apm.agent.sdk.weakconcurrent.DetachedThreadLocal;
22-
import co.elastic.apm.agent.sdk.weakconcurrent.WeakConcurrent;
23-
import co.elastic.apm.agent.sdk.weakconcurrent.WeakMap;
21+
import co.elastic.apm.agent.sdk.internal.pooling.ObjectHandle;
22+
import co.elastic.apm.agent.sdk.internal.pooling.ObjectPool;
23+
import co.elastic.apm.agent.sdk.internal.pooling.ObjectPooling;
2424

2525
import javax.annotation.Nullable;
2626
import java.util.concurrent.Callable;
@@ -44,7 +44,7 @@ public class SignatureParser {
4444
*/
4545
private static final int QUERY_LENGTH_CACHE_UPPER_THRESHOLD = 10_000;
4646

47-
private final DetachedThreadLocal<Scanner> scanner;
47+
private final ObjectPool<? extends ObjectHandle<Scanner>> scannerPool;
4848

4949
/**
5050
* Not using weak keys because ORMs like Hibernate generate equal SQL strings for the same query but don't reuse the same string instance.
@@ -64,20 +64,7 @@ public Scanner call() {
6464
}
6565

6666
public SignatureParser(final Callable<Scanner> scannerAllocator) {
67-
scanner = WeakConcurrent
68-
.<Scanner>threadLocalBuilder()
69-
.withDefaultValueSupplier(new WeakMap.DefaultValueSupplier<Thread, Scanner>() {
70-
@Nullable
71-
@Override
72-
public Scanner getDefaultValue(Thread key) {
73-
try {
74-
return scannerAllocator.call();
75-
} catch (Exception e) {
76-
throw new RuntimeException(e);
77-
}
78-
}
79-
})
80-
.build();
67+
scannerPool = ObjectPooling.createWithDefaultFactory(scannerAllocator);
8168
}
8269

8370
public void querySignature(String query, StringBuilder signature, boolean preparedStatement) {
@@ -98,13 +85,15 @@ public void querySignature(String query, StringBuilder signature, @Nullable Stri
9885
return;
9986
}
10087
}
101-
Scanner scanner = this.scanner.get();
102-
scanner.setQuery(query);
103-
parse(scanner, query, signature, dbLink);
88+
try (ObjectHandle<Scanner> pooledScanner = scannerPool.createInstance()) {
89+
Scanner scanner = pooledScanner.get();
90+
scanner.setQuery(query);
91+
parse(scanner, query, signature, dbLink);
10492

105-
if (cacheable && signatureCache.size() <= DISABLE_CACHE_THRESHOLD) {
106-
// we don't mind a small overshoot due to race conditions
107-
signatureCache.put(query, new String[]{signature.toString(), dbLink != null ? dbLink.toString() : ""});
93+
if (cacheable && signatureCache.size() <= DISABLE_CACHE_THRESHOLD) {
94+
// we don't mind a small overshoot due to race conditions
95+
signatureCache.put(query, new String[]{signature.toString(), dbLink != null ? dbLink.toString() : ""});
96+
}
10897
}
10998
}
11099

apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientHelper.java

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,43 +18,25 @@
1818
*/
1919
package co.elastic.apm.agent.okhttp;
2020

21-
import co.elastic.apm.agent.sdk.state.GlobalState;
22-
import co.elastic.apm.agent.sdk.weakconcurrent.DetachedThreadLocal;
23-
import co.elastic.apm.agent.sdk.weakconcurrent.WeakConcurrent;
24-
import co.elastic.apm.agent.sdk.weakconcurrent.WeakMap;
25-
2621
import javax.annotation.Nullable;
2722

28-
@GlobalState
29-
public class OkHttpClientHelper {
3023

31-
/**
32-
* Used to avoid allocations when calculating destination host name.
33-
*/
34-
public static final DetachedThreadLocal<StringBuilder> destinationHostName = WeakConcurrent
35-
.<StringBuilder>threadLocalBuilder()
36-
.withDefaultValueSupplier(new WeakMap.DefaultValueSupplier<Thread, StringBuilder>() {
37-
@Override
38-
public StringBuilder getDefaultValue(Thread t) {
39-
return new StringBuilder();
40-
}
41-
})
42-
.build();
24+
public class OkHttpClientHelper {
4325

4426
/**
45-
* NOTE: this method returns a StringBuilder instance that is kept as this class's ThreadLocal. Callers of this
46-
* method MAY NOT KEEP A REFERENCE TO THE RETURNED OBJECT, only copy its contents.
27+
* If this method needs to perform corrections on the hostname, it has to allocate a new StringBuilder.
28+
* We accept this due to the fact that this method is called once per HTTP call, making the allocation neglectable
29+
* overhead compared to the allocations performed for the HTTP call itself.
4730
*
4831
* @param originalHostName the original host name retrieved from the OkHttp client
49-
* @return a StringBuilder instance that is kept as a ThreadLocal
32+
* @return the potentially corrected host name
5033
*/
5134
@Nullable
5235
public static CharSequence computeHostName(@Nullable String originalHostName) {
5336
CharSequence hostName = originalHostName;
5437
// okhttp represents IPv6 addresses without square brackets, as opposed to all others, so we should add them
5538
if (originalHostName != null && originalHostName.contains(":") && !originalHostName.startsWith("[")) {
56-
StringBuilder sb = destinationHostName.get();
57-
sb.setLength(0);
39+
StringBuilder sb = new StringBuilder(originalHostName.length() + 2);
5840
sb.append("[").append(originalHostName).append("]");
5941
hostName = sb;
6042
}

0 commit comments

Comments
 (0)