Skip to content

Commit 26ab78d

Browse files
authored
Limit attribute cache sizes on Otel metrics bridge (#4123)
1 parent bbae9e2 commit 26ab78d

File tree

3 files changed

+52
-15
lines changed

3 files changed

+52
-15
lines changed

CHANGELOG.next-release.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ This file contains all changes which are not released yet.
99

1010
# Fixes
1111
<!--FIXES-START-->
12-
12+
* Prevent potential memory pressure by limiting OpenTelemetry metrics bridge attribute cache sizes - [#4123](https://github.com/elastic/apm-agent-java/pull/4123)
1313
<!--FIXES-END-->
1414
# Features and enhancements
1515
<!--ENHANCEMENTS-START-->

apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-metrics-bridge-parent/apm-opentelemetry-metrics-bridge-v1_14/src/main/java/co/elastic/apm/agent/opentelemetry/metrics/bridge/BridgeFactoryV1_14.java

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,15 @@ public class BridgeFactoryV1_14 implements BridgeFactory {
108108

109109
private static volatile BridgeFactoryV1_14 instance;
110110

111-
private final WeakMap<AttributeKey<?>, ProxyAttributeKey<?>> convertedAttributeKeys;
112-
private final WeakMap<io.opentelemetry.api.common.Attributes, ProxyAttributes> convertedAttributes;
111+
// Visible for testing
112+
static final int MAX_ATTRIBUTE_KEY_CACHE_SIZE = 2048;
113+
// Visible for testing
114+
static final int MAX_ATTRIBUTE_CACHE_SIZE = 2048;
115+
116+
// Visible for testing
117+
final WeakMap<AttributeKey<?>, ProxyAttributeKey<?>> convertedAttributeKeys;
118+
// Visible for testing
119+
final WeakMap<io.opentelemetry.api.common.Attributes, ProxyAttributes> convertedAttributes;
113120

114121
public BridgeFactoryV1_14() {
115122
convertedAttributeKeys = WeakConcurrent.buildMap();
@@ -131,14 +138,15 @@ public static void activate(BridgeFactoryV1_14 instanceToActivate) {
131138
}
132139

133140
public final ProxyAttributes convertAttributes(Attributes attributes) {
134-
ProxyAttributes cached = convertedAttributes.get(attributes);
135-
if (cached == null) {
136-
cached = doConvertAttributes(attributes);
137-
if (cached != null) {
138-
convertedAttributes.put(attributes, cached);
141+
ProxyAttributes result = convertedAttributes.get(attributes);
142+
if (result == null) {
143+
result = doConvertAttributes(attributes);
144+
// A slight overshoot of the limit is possible due to concurrency, but this is fine for us
145+
if (result != null && convertedAttributes.approximateSize() < MAX_ATTRIBUTE_CACHE_SIZE) {
146+
convertedAttributes.put(attributes, result);
139147
}
140148
}
141-
return cached;
149+
return result;
142150
}
143151

144152
@SuppressWarnings({"rawtypes", "unchecked"})
@@ -157,14 +165,15 @@ protected ProxyAttributes doConvertAttributes(Attributes attributes) {
157165

158166
@Nullable
159167
public ProxyAttributeKey<?> convertAttributeKey(AttributeKey<?> key) {
160-
ProxyAttributeKey<?> cached = convertedAttributeKeys.get(key);
161-
if (cached == null) {
162-
cached = doConvertAttributeKey(key);
163-
if (cached != null) {
164-
convertedAttributeKeys.put(key, cached);
168+
ProxyAttributeKey<?> result = convertedAttributeKeys.get(key);
169+
if (result == null) {
170+
result = doConvertAttributeKey(key);
171+
// A slight overshoot of the limit is possible due to concurrency, but this is fine for us
172+
if (result != null && convertedAttributeKeys.approximateSize() < MAX_ATTRIBUTE_KEY_CACHE_SIZE) {
173+
convertedAttributeKeys.put(key, result);
165174
}
166175
}
167-
return cached;
176+
return result;
168177
}
169178

170179
@Nullable
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package co.elastic.apm.agent.opentelemetry.metrics.bridge;
2+
3+
import io.opentelemetry.api.common.AttributeKey;
4+
import io.opentelemetry.api.common.Attributes;
5+
import org.junit.jupiter.api.Test;
6+
7+
import java.util.List;
8+
import java.util.stream.Collectors;
9+
import java.util.stream.LongStream;
10+
11+
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
12+
13+
public class BridgeFactoryV1_14Test {
14+
15+
@Test
16+
void checkAttributeCachesLimited() {
17+
List<Attributes> attributes = LongStream.range(0, 10_000)
18+
.mapToObj(i -> Attributes.of(AttributeKey.longKey("foo-" + i), i))
19+
.collect(Collectors.toList());
20+
21+
BridgeFactoryV1_14 bridge = new BridgeFactoryV1_14();
22+
attributes.forEach(bridge::convertAttributes);
23+
24+
assertThat(bridge.convertedAttributes.approximateSize()).isEqualTo(BridgeFactoryV1_14.MAX_ATTRIBUTE_CACHE_SIZE);
25+
assertThat(bridge.convertedAttributeKeys.approximateSize()).isEqualTo(BridgeFactoryV1_14.MAX_ATTRIBUTE_KEY_CACHE_SIZE);
26+
}
27+
28+
}

0 commit comments

Comments
 (0)