Skip to content

Commit 270c3c2

Browse files
committed
Fix NATS startup: move worker registry to post-listener auto-config
RqueueNatsAutoConfig runs @AutoConfigureBefore(RqueueListenerAutoConfig), so RqueueConfig does not exist yet when its beans are evaluated. @ConditionalOnBean(RqueueConfig.class) on natsRqueueWorkerRegistry was therefore always false, leaving no RqueueWorkerRegistry bean in the context and causing RqueueQDetailServiceImpl to fail on startup. Fix: extract natsWorkerRegistryStore and natsRqueueWorkerRegistry into a new RqueueNatsListenerAutoConfig that is @AutoConfigureAfter( RqueueListenerAutoConfig), where RqueueConfig is guaranteed to be present. Register the new class in AutoConfiguration.imports. Assisted-By: Claude Code
1 parent 8113645 commit 270c3c2

3 files changed

Lines changed: 90 additions & 18 deletions

File tree

rqueue-spring-boot-starter/src/main/java/com/github/sonus21/rqueue/spring/boot/RqueueNatsAutoConfig.java

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -158,24 +158,6 @@ public NatsKvBucketValidator natsKvBucketValidator(
158158
return new NatsKvBucketValidator(connection, props.isAutoCreateKvBuckets());
159159
}
160160

161-
@Bean
162-
@ConditionalOnMissingBean(com.github.sonus21.rqueue.worker.WorkerRegistryStore.class)
163-
@DependsOn("natsKvBucketValidator")
164-
public com.github.sonus21.rqueue.worker.WorkerRegistryStore natsWorkerRegistryStore(
165-
Connection connection) throws IOException {
166-
return new com.github.sonus21.rqueue.nats.worker.NatsWorkerRegistryStore(connection);
167-
}
168-
169-
@Bean
170-
@ConditionalOnBean(com.github.sonus21.rqueue.config.RqueueConfig.class)
171-
@ConditionalOnMissingBean(com.github.sonus21.rqueue.worker.RqueueWorkerRegistry.class)
172-
public com.github.sonus21.rqueue.worker.RqueueWorkerRegistry natsRqueueWorkerRegistry(
173-
com.github.sonus21.rqueue.config.RqueueConfig rqueueConfig,
174-
com.github.sonus21.rqueue.worker.WorkerRegistryStore workerRegistryStore) {
175-
return new com.github.sonus21.rqueue.worker.RqueueWorkerRegistryImpl(
176-
rqueueConfig, workerRegistryStore);
177-
}
178-
179161
/**
180162
* NATS-side {@link com.github.sonus21.rqueue.repository.MessageBrowsingRepository} powering
181163
* the dashboard's data-explorer panel. JetStream KV doesn't model arbitrary keyed reads, so
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright (c) 2024-2026 Sonu Kumar
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* You may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and limitations under the License.
14+
*
15+
*/
16+
package com.github.sonus21.rqueue.spring.boot;
17+
18+
import com.github.sonus21.rqueue.config.RqueueConfig;
19+
import com.github.sonus21.rqueue.nats.kv.NatsKvBucketValidator;
20+
import com.github.sonus21.rqueue.nats.worker.NatsWorkerRegistryStore;
21+
import com.github.sonus21.rqueue.worker.RqueueWorkerRegistry;
22+
import com.github.sonus21.rqueue.worker.RqueueWorkerRegistryImpl;
23+
import com.github.sonus21.rqueue.worker.WorkerRegistryStore;
24+
import io.nats.client.Connection;
25+
import io.nats.client.JetStream;
26+
import java.io.IOException;
27+
import org.springframework.boot.autoconfigure.AutoConfiguration;
28+
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
29+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
30+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
31+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
32+
import org.springframework.context.annotation.Bean;
33+
import org.springframework.context.annotation.DependsOn;
34+
35+
/**
36+
* Post-listener auto-configuration that wires the NATS-backed worker registry after
37+
* {@link RqueueListenerAutoConfig} has run and the {@link RqueueConfig} bean is available.
38+
*
39+
* <p>{@link RqueueNatsAutoConfig} must run <em>before</em> the listener config so it can supply
40+
* the {@link com.github.sonus21.rqueue.core.spi.MessageBroker} bean in time. However, the worker
41+
* registry depends on {@link RqueueConfig}, which is only created by
42+
* {@link RqueueListenerAutoConfig}. Splitting these two concerns into two auto-configs — one
43+
* before and one after — breaks the ordering deadlock that caused
44+
* {@code @ConditionalOnBean(RqueueConfig.class)} to always evaluate false.
45+
*/
46+
@AutoConfiguration
47+
@AutoConfigureAfter(RqueueListenerAutoConfig.class)
48+
@ConditionalOnClass(JetStream.class)
49+
@ConditionalOnProperty(name = "rqueue.backend", havingValue = "nats")
50+
public class RqueueNatsListenerAutoConfig {
51+
52+
/**
53+
* NATS KV-backed store that persists worker registration entries.
54+
*
55+
* <p>{@code @DependsOn("natsKvBucketValidator")} ensures the KV bucket exists before the store
56+
* tries to bind to it.
57+
*/
58+
@Bean
59+
@ConditionalOnMissingBean(WorkerRegistryStore.class)
60+
@DependsOn("natsKvBucketValidator")
61+
public WorkerRegistryStore natsWorkerRegistryStore(Connection connection) throws IOException {
62+
return new NatsWorkerRegistryStore(connection);
63+
}
64+
65+
/**
66+
* Worker registry backed by NATS KV. {@link RqueueConfig} is guaranteed to be present here
67+
* because this auto-config runs after {@link RqueueListenerAutoConfig}.
68+
*/
69+
@Bean
70+
@ConditionalOnMissingBean(RqueueWorkerRegistry.class)
71+
public RqueueWorkerRegistry natsRqueueWorkerRegistry(
72+
RqueueConfig rqueueConfig, WorkerRegistryStore workerRegistryStore) {
73+
return new RqueueWorkerRegistryImpl(rqueueConfig, workerRegistryStore);
74+
}
75+
76+
/**
77+
* Guard bean that validates KV buckets. Defined here so it is available for
78+
* {@code @DependsOn("natsKvBucketValidator")} references within this config even when the
79+
* primary definition in {@link RqueueNatsAutoConfig} was conditionally skipped.
80+
*
81+
* <p>In practice the primary definition always wins; this is a fallback safety net.
82+
*/
83+
@Bean
84+
@ConditionalOnMissingBean(NatsKvBucketValidator.class)
85+
public NatsKvBucketValidator natsKvBucketValidatorFallback(
86+
Connection connection, RqueueNatsProperties props) {
87+
return new NatsKvBucketValidator(connection, props.isAutoCreateKvBuckets());
88+
}
89+
}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
com.github.sonus21.rqueue.spring.boot.RqueueListenerAutoConfig
22
com.github.sonus21.rqueue.spring.boot.RqueueMetricsAutoConfig
33
com.github.sonus21.rqueue.spring.boot.RqueueNatsAutoConfig
4+
com.github.sonus21.rqueue.spring.boot.RqueueNatsListenerAutoConfig

0 commit comments

Comments
 (0)