Skip to content

Commit 28fedfb

Browse files
fix(config): Cache component providers in DeclarativeConfigContext (#8070)
1 parent fff95e0 commit 28fedfb

File tree

2 files changed

+48
-2
lines changed

2 files changed

+48
-2
lines changed

sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigContext.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class DeclarativeConfigContext {
2626
private final List<Closeable> closeables = new ArrayList<>();
2727
@Nullable private volatile MeterProvider meterProvider;
2828
@Nullable private Resource resource = null;
29+
@Nullable private List<ComponentProvider> componentProviders = null;
2930

3031
// Visible for testing
3132
DeclarativeConfigContext(SpiHelper spiHelper) {
@@ -87,8 +88,9 @@ <T> T loadComponent(Class<T> type, ConfigKeyValue configKeyValue) {
8788
String name = configKeyValue.getKey();
8889
DeclarativeConfigProperties config = configKeyValue.getValue();
8990

90-
// TODO(jack-berg): cache loaded component providers
91-
List<ComponentProvider> componentProviders = spiHelper.load(ComponentProvider.class);
91+
if (componentProviders == null) {
92+
componentProviders = spiHelper.load(ComponentProvider.class);
93+
}
9294
List<ComponentProvider> matchedProviders =
9395
componentProviders.stream()
9496
.filter(

sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigContextTest.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,17 @@
77

88
import static org.assertj.core.api.Assertions.assertThat;
99
import static org.assertj.core.api.Assertions.assertThatCode;
10+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
11+
import static org.mockito.Mockito.spy;
12+
import static org.mockito.Mockito.times;
13+
import static org.mockito.Mockito.verify;
1014

1115
import io.opentelemetry.api.incubator.config.DeclarativeConfigException;
1216
import io.opentelemetry.common.ComponentLoader;
17+
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
18+
import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider;
1319
import io.opentelemetry.sdk.resources.Resource;
20+
import java.util.Collections;
1421
import org.junit.jupiter.api.Test;
1522

1623
class DeclarativeConfigContextTest {
@@ -26,4 +33,41 @@ void resourceMustBeSetBeforeUse() {
2633
context.setResource(resource);
2734
assertThat(context.getResource()).isSameAs(resource);
2835
}
36+
37+
@Test
38+
void componentProvidersCached() {
39+
SpiHelper spiHelper =
40+
spy(SpiHelper.create(DeclarativeConfigContextTest.class.getClassLoader()));
41+
DeclarativeConfigContext context = new DeclarativeConfigContext(spiHelper);
42+
43+
ComponentLoader componentLoader =
44+
ComponentLoader.forClassLoader(DeclarativeConfigContextTest.class.getClassLoader());
45+
46+
// First loadComponent call should load providers
47+
assertThatThrownBy(
48+
() ->
49+
context.loadComponent(
50+
Resource.class,
51+
ConfigKeyValue.of(
52+
"nonexistent",
53+
YamlDeclarativeConfigProperties.create(
54+
Collections.emptyMap(), componentLoader))))
55+
.isInstanceOf(DeclarativeConfigException.class)
56+
.hasMessageContaining("No component provider detected");
57+
58+
// Second loadComponent call should use cached providers, not reload
59+
assertThatThrownBy(
60+
() ->
61+
context.loadComponent(
62+
Resource.class,
63+
ConfigKeyValue.of(
64+
"another",
65+
YamlDeclarativeConfigProperties.create(
66+
Collections.emptyMap(), componentLoader))))
67+
.isInstanceOf(DeclarativeConfigException.class)
68+
.hasMessageContaining("No component provider detected");
69+
70+
// Verify spiHelper.load() was only called once
71+
verify(spiHelper, times(1)).load(ComponentProvider.class);
72+
}
2973
}

0 commit comments

Comments
 (0)