Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spring 6.1, NPE for anonymous objects with @Scheduler annotation #31980

Closed
tomacco opened this issue Jan 8, 2024 · 3 comments
Closed

Spring 6.1, NPE for anonymous objects with @Scheduler annotation #31980

tomacco opened this issue Jan 8, 2024 · 3 comments
Labels
status: duplicate A duplicate of another issue theme: observability An issue related to observability and tracing

Comments

@tomacco
Copy link

tomacco commented Jan 8, 2024

Affects: 6.1
Context: Upgrading from Spring Boot 3.1 (Spring 6.0) to Spring Boot 3.2 (Spring 6.1)

We encountered an issue with Beans defined as anonymous objects containing a single method annotated with @Scheduled. For instance:

    @Bean
    fun somethingCoolStreamConsumerScheduler(
        coolStreamConsumer: CoolStreamConsumer,
        coolnessChecker: CoolnessChecker,
    ) = object {

        @Scheduled(fixedDelayString = "\${kafka.consumers.coolStuff.fixedDelay}")
        fun execute() = coolStreamConsumer.consume()
    }

Upon execution, these tasks failed to initialize, resulting in the following stack trace:

java.lang.NullPointerException: null
	at java.base/java.util.Objects.requireNonNull(Objects.java:209) ~[?:?]
	at io.micrometer.common.ImmutableKeyValue.<init>(ImmutableKeyValue.java:38) ~[micrometer-commons-1.12.2-SNAPSHOT.jar:1.12.2-SNAPSHOT]
	at io.micrometer.common.KeyValue.of(KeyValue.java:48) ~[micrometer-commons-1.12.2-SNAPSHOT.jar:1.12.2-SNAPSHOT]
	at io.micrometer.common.KeyValue.of(KeyValue.java:58) ~[micrometer-commons-1.12.2-SNAPSHOT.jar:1.12.2-SNAPSHOT]
	at org.springframework.scheduling.support.DefaultScheduledTaskObservationConvention.codeNamespace(DefaultScheduledTaskObservationConvention.java:64) ~[spring-context-6.1.2.jar:6.1.2]
	at org.springframework.scheduling.support.DefaultScheduledTaskObservationConvention.getLowCardinalityKeyValues(DefaultScheduledTaskObservationConvention.java:56) ~[spring-context-6.1.2.jar:6.1.2]
	at org.springframework.scheduling.support.DefaultScheduledTaskObservationConvention.getLowCardinalityKeyValues(DefaultScheduledTaskObservationConvention.java:31) ~[spring-context-6.1.2.jar:6.1.2]
	at io.micrometer.observation.SimpleObservation.start(SimpleObservation.java:152) ~[micrometer-observation-1.12.2-SNAPSHOT.jar:1.12.2-SNAPSHOT]
	at io.micrometer.observation.Observation.observe(Observation.java:497) ~[micrometer-observation-1.12.2-SNAPSHOT.jar:1.12.2-SNAPSHOT]
	at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:124) ~[spring-context-6.1.2.jar:6.1.2]
	at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) [spring-context-6.1.2.jar:6.1.2]

After investigating and debugging, we found that in this Spring version...

@scheduled methods are now instrumented for observability.

Our issue arises because micrometer SimpleObservation.start requires the class canonicalName, which returns null for our anonymous objects.

We resolved this by using named classes. However, I want to highlight this case as in our case a MINOR update has meant a lot of changes in our code base, and there does not seem to be a way to disable this, now default, behaviour in Spring 6.1.

Looking forward to your feedback.


@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jan 8, 2024
@tomacco
Copy link
Author

tomacco commented Jan 8, 2024

Update, ok I've seen you have already corrected this behaviour

	protected KeyValue codeNamespace(ScheduledTaskObservationContext context) {
		if (context.getTargetClass().getCanonicalName() != null) {
			return KeyValue.of(LowCardinalityKeyNames.CODE_NAMESPACE, context.getTargetClass().getCanonicalName());
		}
		return CODE_NAMESPACE_ANONYMOUS;
	}

Closing the issue.

@tomacco tomacco closed this as completed Jan 8, 2024
@bclozel
Copy link
Member

bclozel commented Jan 8, 2024

As you've found out, this is a duplicate of #31918, which is already solved in 6.1.3-SNAPSHOT.

We resolved this by using named classes. However, I want to highlight this case as in our case a MINOR update has meant a lot of changes in our code base, and there does not seem to be a way to disable this, now default, behaviour in Spring 6.1.

I think you can disable this with an observation predicate in the meantime:

import io.micrometer.observation.ObservationPredicate;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class ObservationConfig {

    @Bean
    public ObservationPredicate disableScheduledObservation() {
        return (name, context) -> "tasks.scheduled.execution".equals(name);
    }
    
}

Care to elaborate about the changes in your codebase? Usually minor Spring Framework releases come with new features and few deprecations and required code changes. I'd be curious to know which Framework changes caused pain. Now about this particular change, this is a bug in a new feature that was not caught during the milestone phase.

@bclozel bclozel added status: duplicate A duplicate of another issue theme: observability An issue related to observability and tracing and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Jan 8, 2024
@bclozel bclozel closed this as not planned Won't fix, can't repro, duplicate, stale Jan 8, 2024
@tomacco
Copy link
Author

tomacco commented Jan 8, 2024

@bclozel as we didn't know how to disable this, our first approach was to create normal classes so the class.getCanonicalName() won't return null. It required more work than expected basically because we have several methods annotated with this annotation.
Certainly your suggestion is way better. Finally what we are going to do is to wait until this is part of Spring Boot 3.

Thank you for your time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: duplicate A duplicate of another issue theme: observability An issue related to observability and tracing
Projects
None yet
Development

No branches or pull requests

3 participants