Skip to content

Performance issue in SpringConfigProperties#getInt due to lack of caching #15009

@wyctxwd1

Description

@wyctxwd1

Describe the bug

cpu-flame.html

Image

I uploaded a flamegraph showing a CPU hotspot during log processing, From the flamegraph, we can see that logging is slow, and the root cause is:

io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.SpringConfigProperties#getInt

Because AutoConfiguredOpenTelemetrySdk does not have @ConditionalOnMissingBean, I cannot override SpringConfigProperties gracefully.

Image

Temporary workaround I implemented:

I wrapped SpringConfigProperties with a caching layer that handles null values using Optional:

`public class CacheProperties implements ConfigProperties {
// ConcurrentHashMap don't cache null
private final Map<String, Optional> cache = new ConcurrentHashMap<>();

private SpringConfigProperties delegate;

public CacheProperties(SpringConfigProperties springConfigProperties) {
    this.delegate = springConfigProperties;
}

@Nullable
@Override
public String getString(String name) {
    return (String) cache.computeIfAbsent(name, k -> Optional.ofNullable(delegate.getString(name))).orElse(null);
}

@Override
public Map<String, String> getMap(String name) {
    return (Map<String, String>) cache.computeIfAbsent(name, k -> Optional.ofNullable(delegate.getMap(name))).orElse(null);
}

@Nullable
@Override
public Duration getDuration(String name) {
    return (Duration) cache.computeIfAbsent(name, k -> Optional.ofNullable(delegate.getDuration(name))).orElse(null);
}

@Override
public List<String> getList(String name) {
    return (List<String>) cache.computeIfAbsent(name, k -> Optional.ofNullable(delegate.getList(name))).orElse(null);
}

@Nullable
@Override
public Double getDouble(String name) {
    return (Double) cache.computeIfAbsent(name, k -> Optional.ofNullable(delegate.getDouble(name))).orElse(null);
}

@Nullable
@Override
public Integer getInt(String name) {
    return (Integer) cache.computeIfAbsent(name, k -> Optional.ofNullable(delegate.getInt(name))).orElse(null);
}

@Nullable
@Override
public Long getLong(String name) {
    return (Long) cache.computeIfAbsent(name, k -> Optional.ofNullable(delegate.getLong(name))).orElse(null);
}

@Nullable
@Override
public Boolean getBoolean(String name) {
    return (Boolean) cache.computeIfAbsent(name, k -> Optional.ofNullable(delegate.getBoolean(name))).orElse(null);
}

}`

And then I updated the factory method:

public static ConfigProperties create(
        Environment env,
        OtlpExporterProperties otlpExporterProperties,
        OtelResourceProperties resourceProperties,
        OtelSpringProperties otelSpringProperties,
        ConfigProperties fallback) {

    return new CacheProperties(new SpringConfigProperties(
            env,
            new SpelExpressionParser(),
            otlpExporterProperties,
            resourceProperties,
            otelSpringProperties,
            fallback));
}

This workaround greatly improves performance for repeated property lookups.

Steps to reproduce

Configure OpenTelemetry with AutoConfiguredOpenTelemetrySdk in a Spring Boot application.

Enable logging at INFO/DEBUG level so that many logs are emitted.

Emit a large number of log statements (e.g., loop over forEach on a collection of log entries).

Observe that SpringConfigProperties#getInt becomes a CPU hotspot in profiling or flamegraphs.

cpu-flame.html

Expected behavior

Accessing configuration properties via SpringConfigProperties should be efficient and not incur high CPU usage during frequent logging.

The auto-configuration should allow overriding or replacing SpringConfigProperties gracefully, for example by supporting @ConditionalOnMissingBean, so users can provide a cached or customized implementation without having to fork the library.

Actual behavior

Frequent access to configuration properties via SpringConfigProperties causes significant CPU usage and slows down logging. The class is eagerly instantiated and does not support graceful overriding because AutoConfiguredOpenTelemetrySdk does not use @ConditionalOnMissingBean, forcing users to implement workarounds like wrapping it with a custom caching layer.

Javaagent or library instrumentation version

io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom:2.20.1

Environment

JDK: Azul Zulu 21.0.7
OS: macos

Additional context

No response

Tip

React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it. Learn more here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions