From 1d73441ca934fd33887fb81261cfa48f08ad6ccf Mon Sep 17 00:00:00 2001 From: j-sandy <30489233+j-sandy@users.noreply.github.com> Date: Mon, 19 Feb 2024 19:37:01 +0530 Subject: [PATCH] fix(core): remove circular reference from CanaryJudgeTask, CompareJudgeResultsTask, MetricSetMixerServiceTask, MonitorCanaryTask and RunCanaryTask classes identified during upgrade to spring boot 2.6.x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While upgrading spring boot 2.6.15 and spring cloud 2021.0.8, encountered below errors in kayenta-integration-tests module during test execution and 8 tests failed with similar error: ``` Failed to load ApplicationContext java.lang.IllegalStateException: Failed to load ApplicationContext at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:98) at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124) at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:118) at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83) at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.java:43) at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:248) at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:138) ... Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'redisOrcaQueueConfiguration': Unsatisfied dependency expressed through method 'redisQueueObjectMapper' parameter 2; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'taskResolver' defined in class path resource [com/netflix/spinnaker/orca/config/OrcaConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.netflix.spinnaker.orca.TaskResolver]: Factory method 'taskResolver' threw exception; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'canaryJudgeTask' defined in URL [jar:file:/kayenta/kayenta-core/build/libs/kayenta-core.jar!/com/netflix/kayenta/canary/orca/CanaryJudgeTask.class]: Unsatisfied dependency expressed through constructor parameter 4; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'executionMapper' defined in URL [jar:file:/kayenta/kayenta-core/build/libs/kayenta-core.jar!/com/netflix/kayenta/canary/ExecutionMapper.class]: Unsatisfied dependency expressed through constructor parameter 4; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'executionLauncher' defined in URL [jar:file:/.m2/repository/io/spinnaker/orca/orca-core/sc202108-sb2615-SNAPSHOT/orca-core-sc202108-sb2615-SNAPSHOT.jar!/com/netflix/spinnaker/orca/pipeline/ExecutionLauncher.class]: Unsatisfied dependency expressed through constructor parameter 2; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'queueExecutionRunner' defined in URL [jar:file:/.m2/repository/io/spinnaker/orca/orca-queue/sc202108-sb2615-SNAPSHOT/orca-queue-sc202108-sb2615-SNAPSHOT.jar!/com/netflix/spinnaker/orca/q/QueueExecutionRunner.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'redisOrcaQueueConfiguration': Requested bean is currently in creation: Is there an unresolvable circular reference? at app//org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:768) at app//org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:720) at app//org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) at app//org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431) at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619) at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) at app//org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) at app//org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) at app//org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) at app//org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) at app//org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955) at app//org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:920) at app//org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) at app//org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) at app//org.springframework.boot.SpringApplication.refresh(SpringApplication.java:745) at app//org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:423) at app//org.springframework.boot.SpringApplication.run(SpringApplication.java:307) at app//org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:148) at app//org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:141) at app//org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:90) ... 88 more ``` And the logs were showing below details about circular reference: ``` 2024-02-16 23:00:59.572 ERROR 833552 --- [ Test worker] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPLICATION FAILED TO START *************************** Description: The dependencies of some of the beans in the application context form a cycle: ┌─────┐ | redisOrcaQueueConfiguration ↑ ↓ | taskResolver defined in class path resource [com/netflix/spinnaker/orca/config/OrcaConfiguration.class] ↑ ↓ | canaryJudgeTask defined in URL [jar:file:/kayenta/kayenta-core/build/libs/kayenta-core.jar!/com/netflix/kayenta/canary/orca/CanaryJudgeTask.class] ↑ ↓ | executionMapper defined in URL [jar:file:/kayenta/kayenta-core/build/libs/kayenta-core.jar!/com/netflix/kayenta/canary/ExecutionMapper.class] ↑ ↓ | executionLauncher defined in URL [jar:file:/.m2/repository/io/spinnaker/orca/orca-core/sc202108-sb2615-SNAPSHOT/orca-core-sc202108-sb2615-SNAPSHOT.jar!/com/netflix/spinnaker/orca/pipeline/ExecutionLauncher.class] ↑ ↓ | queueExecutionRunner defined in URL [jar:file:/.m2/repository/io/spinnaker/orca/orca-queue/sc202108-sb2615-SNAPSHOT/orca-queue-sc202108-sb2615-SNAPSHOT.jar!/com/netflix/spinnaker/orca/q/QueueExecutionRunner.class] └─────┘ Action: Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true. ``` Similar errors were reported for CanaryJudgeTask, CompareJudgeResultsTask, MetricSetMixerServiceTask, MonitorCanaryTask and RunCanaryTask classes, until fixed for each one of them. In order to fix the issue, deferred the initialization by using @Lazy annotation for above mentioned classes. --- .../java/com/netflix/kayenta/canary/orca/CanaryJudgeTask.java | 2 ++ .../netflix/kayenta/canary/orca/CompareJudgeResultsTask.java | 2 ++ .../netflix/kayenta/metrics/orca/MetricSetMixerServiceTask.java | 2 ++ .../standalonecanaryanalysis/orca/task/MonitorCanaryTask.java | 2 ++ .../standalonecanaryanalysis/orca/task/RunCanaryTask.java | 2 ++ 5 files changed, 10 insertions(+) diff --git a/kayenta-core/src/main/java/com/netflix/kayenta/canary/orca/CanaryJudgeTask.java b/kayenta-core/src/main/java/com/netflix/kayenta/canary/orca/CanaryJudgeTask.java index 40b9787dc..060333a85 100644 --- a/kayenta-core/src/main/java/com/netflix/kayenta/canary/orca/CanaryJudgeTask.java +++ b/kayenta-core/src/main/java/com/netflix/kayenta/canary/orca/CanaryJudgeTask.java @@ -36,6 +36,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @Slf4j @@ -48,6 +49,7 @@ public class CanaryJudgeTask implements RetryableTask { private final ObjectMapper objectMapper; private final ExecutionMapper executionMapper; + @Lazy @Autowired public CanaryJudgeTask( AccountCredentialsRepository accountCredentialsRepository, diff --git a/kayenta-core/src/main/java/com/netflix/kayenta/canary/orca/CompareJudgeResultsTask.java b/kayenta-core/src/main/java/com/netflix/kayenta/canary/orca/CompareJudgeResultsTask.java index 96fb4badf..cbb6af0e0 100644 --- a/kayenta-core/src/main/java/com/netflix/kayenta/canary/orca/CompareJudgeResultsTask.java +++ b/kayenta-core/src/main/java/com/netflix/kayenta/canary/orca/CompareJudgeResultsTask.java @@ -33,6 +33,7 @@ import javax.annotation.Nonnull; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @Slf4j @@ -45,6 +46,7 @@ public class CompareJudgeResultsTask implements RetryableTask { private final ObjectMapper objectMapper; private final ExecutionMapper executionMapper; + @Lazy @Autowired public CompareJudgeResultsTask( AccountCredentialsRepository accountCredentialsRepository, diff --git a/kayenta-core/src/main/java/com/netflix/kayenta/metrics/orca/MetricSetMixerServiceTask.java b/kayenta-core/src/main/java/com/netflix/kayenta/metrics/orca/MetricSetMixerServiceTask.java index 33dfffc43..0a9a77e0d 100644 --- a/kayenta-core/src/main/java/com/netflix/kayenta/metrics/orca/MetricSetMixerServiceTask.java +++ b/kayenta-core/src/main/java/com/netflix/kayenta/metrics/orca/MetricSetMixerServiceTask.java @@ -37,6 +37,7 @@ import javax.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @Component @@ -47,6 +48,7 @@ public class MetricSetMixerServiceTask implements RetryableTask { private final MetricSetMixerService metricSetMixerService; private final ExecutionMapper executionMapper; + @Lazy @Autowired public MetricSetMixerServiceTask( AccountCredentialsRepository accountCredentialsRepository, diff --git a/kayenta-standalone-canary-analysis/src/main/java/com/netflix/kayenta/standalonecanaryanalysis/orca/task/MonitorCanaryTask.java b/kayenta-standalone-canary-analysis/src/main/java/com/netflix/kayenta/standalonecanaryanalysis/orca/task/MonitorCanaryTask.java index 9372a143c..f0525c9e1 100644 --- a/kayenta-standalone-canary-analysis/src/main/java/com/netflix/kayenta/standalonecanaryanalysis/orca/task/MonitorCanaryTask.java +++ b/kayenta-standalone-canary-analysis/src/main/java/com/netflix/kayenta/standalonecanaryanalysis/orca/task/MonitorCanaryTask.java @@ -48,6 +48,7 @@ import javax.annotation.Nonnull; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; /** @@ -68,6 +69,7 @@ public class MonitorCanaryTask implements Task, OverridableTimeoutRetryableTask private final AccountCredentialsRepository accountCredentialsRepository; private final ExecutionMapper executionMapper; + @Lazy @Autowired public MonitorCanaryTask( ExecutionRepository executionRepository, diff --git a/kayenta-standalone-canary-analysis/src/main/java/com/netflix/kayenta/standalonecanaryanalysis/orca/task/RunCanaryTask.java b/kayenta-standalone-canary-analysis/src/main/java/com/netflix/kayenta/standalonecanaryanalysis/orca/task/RunCanaryTask.java index 2238f08f1..9ff9931b9 100644 --- a/kayenta-standalone-canary-analysis/src/main/java/com/netflix/kayenta/standalonecanaryanalysis/orca/task/RunCanaryTask.java +++ b/kayenta-standalone-canary-analysis/src/main/java/com/netflix/kayenta/standalonecanaryanalysis/orca/task/RunCanaryTask.java @@ -34,6 +34,7 @@ import javax.annotation.Nonnull; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; /** Orca Task that tells Kayenta to execute a canary analysis / judgement */ @@ -47,6 +48,7 @@ public class RunCanaryTask implements Task { private final ExecutionMapper executionMapper; private final ObjectMapper kayentaObjectMapper; + @Lazy @Autowired public RunCanaryTask( AccountCredentialsRepository accountCredentialsRepository,