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

MeterRegistryPostProcessor dependency on ApplicationContext causes early context initialization #40525

Open
ilx opened this issue Apr 25, 2024 · 9 comments
Labels
status: feedback-reminder We've sent a reminder that we need additional information before we can continue status: waiting-for-feedback We need additional information before we can continue status: waiting-for-triage An issue we've not yet triaged

Comments

@ilx
Copy link

ilx commented Apr 25, 2024

With recent spring-boot-actuator-autoconfigure-3.2.4.jar BeanPostProcessorChecker produces warnings when we apply MetricsAutoConfiguration:

2024-04-25 17:59:57.980 [main] {} WARN  org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean 'distributedFeaturesConfiguration' of type [com.xebialabs.xlrelease.features.distributed.DistributedFeaturesConfiguration$$SpringCGLIB$$0] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [meterRegistryPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.

The stacktrace of the 1st time it ends up in org.springframework.context.support.PostProcessorRegistrationDelegate.BeanPostProcessorChecker#postProcessAfterInitialization is:

Breakpoint reached
	at org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization(PostProcessorRegistrationDelegate.java:437)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:438)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1789)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
	at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$766/0x00000008015fe6f0.getObject(Unknown Source:-1)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:409)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1335)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1165)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
	at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$766/0x00000008015fe6f0.getObject(Unknown Source:-1)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1443)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1353)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:904)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:782)
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:237)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1355)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1192)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
	at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$766/0x00000008015fe6f0.getObject(Unknown Source:-1)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:409)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1335)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1165)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
	at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$766/0x00000008015fe6f0.getObject(Unknown Source:-1)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:365)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:135)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:696)
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:203)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1355)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1192)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getSingletonFactoryBeanForTypeCheck(AbstractAutowireCapableBeanFactory.java:996)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBean(AbstractAutowireCapableBeanFactory.java:891)
	at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:621)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:575)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:534)
	at org.springframework.beans.factory.BeanFactoryUtils.beanNamesForTypeIncludingAncestors(BeanFactoryUtils.java:260)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1637)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1397)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1353)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:904)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:782)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:542)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1335)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1165)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
	at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$766/0x00000008015fe6f0.getObject(Unknown Source:-1)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205)
	at org.springframework.context.support.PostProcessorRegistrationDelegate.registerBeanPostProcessors(PostProcessorRegistrationDelegate.java:277)
	at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:805)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:608)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:334)
	at com.xebialabs.xlrelease.XLReleaseBootstrapper$.main(XLReleaseBootstrapper.scala:182)
	at com.xebialabs.xlrelease.XLReleaseBootstrapper.main(XLReleaseBootstrapper.scala:-1)

I can see this is caused by meterRegistryPostProcessor trying to resolve application context:

image

I changed MetricsAutoConfiguration and MeterRegistryPostProcessor so that MeterRegistryPostProcessor implements ApplicationContextAware and does not need ApplicationContext as 1st constructor parameter - in that case I was able to avoid early application context initialization.

I cannot give a simple example because this happens in an old legacy app and I was unable to reproduce it outside of it.
If there is way to trace cause of this early initialization please let me know.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Apr 25, 2024
@wilkinsona
Copy link
Member

Unfortunately, the stack trace isn't useful as, while it tells us that a series of beans is being created, it doesn't tell us what they are. You can figure that out using the debugger by walking back up the stack and looking at the value of the beanName parameter for each occurrence of ….createBean(AbstractAutowireCapableBeanFactory.java:522). If you can share with us those bean names plus details of how each is defined, we may be able to figure out the cause.

@wilkinsona wilkinsona added the status: waiting-for-feedback We need additional information before we can continue label Apr 25, 2024
@ilx
Copy link
Author

ilx commented Apr 25, 2024

beans are:

  • meterRegistryPostProcessor
  • actorSystemHolder
  • xlrDatabaseInformation
  • sqlConfiguration
  • databaseProxyFeature
  • distributedFeaturesConfiguration

where actorSystemHolder is (most of the code is Scala codebase):

class ActorSystemHolder(xlrConfig: XlrConfig, @Qualifier("xlrDatabaseInformation") dbInfo: DatabaseInfo)
  extends Logging with ScalaSpringAwareBean with FactoryBean[ActorSystem] {

  private var running: Boolean = false

  lazy val actorSystem: ActorSystem = {
    info("Starting up actor system.")
    if (xlrConfig.isActiveOnStartup) {
      NodeState.setActive(true)
    }

    val pekkoConfig = getSlickConfig.withFallback(xlrConfig.pekko.config)
    val system = ActorSystem(xlrConfig.pekko.systemName, pekkoConfig)
    system.registerExtension(SpringExtension)
    running = true
    system
  }

  def shutdown(): Unit = {
    if (running) {
      info("Initiating actor system shutdown.")
      if (!AutoDowning.isDowning) {
        // Actor system should be terminated by Coordinated Shutdown Pekko extension
        val result = CoordinatedShutdown.get(actorSystem).run(GracefulShutdownReason)
        info("Waiting for actor system shutdown (indefinitely).")
        Await.result(result, atMost = Duration.Inf)
      }
      running = false
      info("Actor system shutdown finished.")
    }
  }

  override def getObject: ActorSystem = actorSystem

  override def getObjectType: Class[_] = classOf[ActorSystem]

DatabaseInfo is just a bean inside SqlConfiguration:

  @Bean
  def xlrDatabaseInformation: DatabaseInfo = {
    DatabaseInfo(xlrRepositoryDataSourceProxy())
  }

where it extracts database information via connection.getMetadata:

object DatabaseInfo {

  def apply(ds: DataSource): DatabaseInfo = {
    map(ds.getConnection) { connection =>
      val metadata = connection.getMetaData
      DatabaseInfo(metadata)
    }
  }
class SqlConfiguration(xlrConfig: XlrConfig,
                       databaseProxyFeature: DatabaseProxyFeature,
                       metricsTrackerFactory: MetricsTrackerFactory
                      )

DatabaseProxyFeature and MetricsTrackerFactory are making it a bit complicated but I can remove them. In that case list of beans is shorter:

  • meterRegistryPostProcessor
  • actorSystemHolder
  • xlrDatabaseInformation
  • sqlConfiguration

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Apr 25, 2024
@wilkinsona
Copy link
Member

Thanks for the details thus far. What's the bean that's being retrieved right at the bottom of the stack?

	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205)
	at org.springframework.context.support.PostProcessorRegistrationDelegate.registerBeanPostProcessors(PostProcessorRegistrationDelegate.java:277)

@wilkinsona wilkinsona added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Apr 25, 2024
@ilx
Copy link
Author

ilx commented Apr 25, 2024

Bean at the bottom of the stack is meterRegistryPostProcessor

image

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Apr 25, 2024
@ilx
Copy link
Author

ilx commented Apr 25, 2024

Hmmm I noticed that the bean itself is configured via xml config:

    <bean id="actorSystemHolder" class="com.xebialabs.xlrelease.actors.ActorSystemHolder" destroy-method="shutdown"
          depends-on="beforeLiquibaseUpgrade">
        <constructor-arg name="xlrConfig" ref="xlrConfig"/>
        <constructor-arg name="dbInfo" ref="xlrDatabaseInformation" />
    </bean>

not sure if that makes difference

@wilkinsona
Copy link
Member

wilkinsona commented Apr 26, 2024

It being defined in XML may make a difference. It may mean that the bean factory can't tell statically what type the com.xebialabs.xlrelease.actors.ActorSystemHolder factory bean will produce so it's being created really early to figure that out. Defining it in an @Configuration class may help with that as may using ObjectProvider to injects its arguments as injection into a FactoryBean is a known cause of problems:

The XML snippet also shows that beforeLiquibaseUpgrade is involved here too. That will affect things.

I'm afraid that we're going to need the complete picture here – and ideally a sample that reproduces the problem – to make any further progress.

Injecting the application context into the constructor should not be a problem as there's special handling for that and it isn't treated like a normal bean. We can't change that without a complete understanding of the problem as it may not be the optimal solution and it may have unwanted side-effects that we couldn't justify without a full understanding.

@wilkinsona wilkinsona added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Apr 26, 2024
@ilx
Copy link
Author

ilx commented Apr 26, 2024

TBH I suspect it has something to do with XML vs Annotation config.
It would take me some time to create reproducible sample (as this just works elsewhere).

In the meantime, i have traced this to the applicationContext passed via org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration#meterRegistryPostProcessor

Maybe it would be fine to make this postprocessor ApplicationContextAware and invoke hasNoCompositeMeterRegistryBeans once postProcessAfterInitialization is invoked?

i.e.

class MeterRegistryPostProcessor implements BeanPostProcessor, SmartInitializingSingleton, ApplicationContextAware {

	private final ObjectProvider<MetricsProperties> properties;

	private final ObjectProvider<MeterRegistryCustomizer<?>> customizers;

	private final ObjectProvider<MeterFilter> filters;

	private final ObjectProvider<MeterBinder> binders;

	private volatile boolean deferBinding = true;

	private final Set<MeterRegistry> deferredBindings = new LinkedHashSet<>();
	
	private ApplicationContext applicationContext;

	private boolean hasNoCompositeMeterRegistryBeans(ApplicationContext applicationContext) {
		return applicationContext.getBeanNamesForType(CompositeMeterRegistry.class, false, false).length == 0;
	}

	MeterRegistryPostProcessor(ObjectProvider<MetricsProperties> properties,
	                           ObjectProvider<MeterRegistryCustomizer<?>> customizers, ObjectProvider<MeterFilter> filters,
	                           ObjectProvider<MeterBinder> binders) {
		this.properties = properties;
		this.customizers = customizers;
		this.filters = filters;
		this.binders = binders;

	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if (bean instanceof MeterRegistry meterRegistry) {
			postProcessMeterRegistry(meterRegistry);
		}
		return bean;
	}

	@Override
	public void afterSingletonsInstantiated() {
		synchronized (this.deferredBindings) {
			this.deferBinding = false;
			this.deferredBindings.forEach(this::applyBinders);
		}
	}

	private void postProcessMeterRegistry(MeterRegistry meterRegistry) {
		// Customizers must be applied before binders, as they may add custom tags or
		// alter timer or summary configuration.
		applyCustomizers(meterRegistry);
		applyFilters(meterRegistry);
		addToGlobalRegistryIfNecessary(meterRegistry);
		if (isBindable(meterRegistry)) {
			applyBinders(meterRegistry);
		}
	}

	@SuppressWarnings("unchecked")
	private void applyCustomizers(MeterRegistry meterRegistry) {
		List<MeterRegistryCustomizer<?>> customizers = this.customizers.orderedStream().toList();
		LambdaSafe.callbacks(MeterRegistryCustomizer.class, customizers, meterRegistry)
				.withLogger(MeterRegistryPostProcessor.class)
				.invoke((customizer) -> customizer.customize(meterRegistry));
	}

	private void applyFilters(MeterRegistry meterRegistry) {
		if (meterRegistry instanceof AutoConfiguredCompositeMeterRegistry) {
			return;
		}
		this.filters.orderedStream().forEach(meterRegistry.config()::meterFilter);
	}

	private void addToGlobalRegistryIfNecessary(MeterRegistry meterRegistry) {
		if (this.properties.getObject().isUseGlobalRegistry() && !isGlobalRegistry(meterRegistry)) {
			Metrics.addRegistry(meterRegistry);
		}
	}

	private boolean isGlobalRegistry(MeterRegistry meterRegistry) {
		return meterRegistry == Metrics.globalRegistry;
	}

	private boolean isBindable(MeterRegistry meterRegistry) {
		return hasNoCompositeMeterRegistryBeans(applicationContext) || isCompositeMeterRegistry(meterRegistry);
	}

	@Override
	public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}

	private boolean isCompositeMeterRegistry(MeterRegistry meterRegistry) {
		return meterRegistry instanceof CompositeMeterRegistry;
	}

	void applyBinders(MeterRegistry meterRegistry) {
		if (this.deferBinding) {
			synchronized (this.deferredBindings) {
				if (this.deferBinding) {
					this.deferredBindings.add(meterRegistry);
					return;
				}
			}
		}
		this.binders.orderedStream().forEach((binder) -> binder.bindTo(meterRegistry));
	}


}

I tried this and it does not cause the checker to fail and I do not see any problems.
I'm not sure if there is another post processor that uses the same pattern...

Is there a chance to accept this change or at least somehow make processor instantiation "tweakable" (maybe check presence of the postprocessor with same name)?

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Apr 26, 2024
@wilkinsona
Copy link
Member

I tried this and it does not cause the checker to fail and I do not see any problems. I'm not sure if there is another post processor that uses the same pattern...

Is there a chance to accept this change

I tried to address this at the end of my previous comment:

Injecting the application context into the constructor should not be a problem as there's special handling for that and it isn't treated like a normal bean. We can't change that without a complete understanding of the problem as it may not be the optimal solution and it may have unwanted side-effects that we couldn't justify without a full understanding.

I am still of the opinion that we should not change this without a full understanding of the problem. How the ApplicationContext is injected should not make any difference. Apparently it is making a difference so there's something unknown here that we don't understand. Until we do, it would be hard to justify making a change.

somehow make processor instantiation "tweakable" (maybe check presence of the postprocessor with same name)

You could perhaps use a bean factory post process to replace the definition of the MeterRegistryPostProcessor. In your situation, I would invest time in creating a reproducer rather than resorting to such a hack.

@wilkinsona wilkinsona added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Apr 26, 2024
@spring-projects-issues
Copy link
Collaborator

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

@spring-projects-issues spring-projects-issues added the status: feedback-reminder We've sent a reminder that we need additional information before we can continue label May 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: feedback-reminder We've sent a reminder that we need additional information before we can continue status: waiting-for-feedback We need additional information before we can continue status: waiting-for-triage An issue we've not yet triaged
Projects
None yet
Development

No branches or pull requests

3 participants