Skip to content

Commit 848e23d

Browse files
committed
Add NATS worker registry, KV bucket validator, autoCreateKvBuckets flag
- Introduce backend-agnostic WorkerRegistryStore SPI in rqueue-core; relocate RqueueWorkerRegistryImpl out of rqueue-redis. Redis impl wraps RqueueRedisTemplate hash ops; NATS impl uses two JetStream KV buckets (rqueue-workers, rqueue-worker-heartbeats) with hash-of-strings emulated as flattened keys. - Centralize NATS KV bucket names in NatsKvBuckets (six buckets: rqueue-queue-config, rqueue-jobs, rqueue-locks, rqueue-message-metadata, rqueue-workers, rqueue-worker-heartbeats). Each store/dao references the constant; ALL_BUCKETS drives validation. - Add NatsKvBucketValidator and rqueue.nats.autoCreateKvBuckets property (sourced from RqueueNatsProperties; rqueue-nats never reads keys directly). Two-layer enforcement: inline call in the natsConnection bean factory so validation completes during Connection bean creation, plus @dependsOn("natsKvBucketValidator") on every NATS-coupled bean. - Document the bucket list, pre-create commands for restricted JetStream accounts, and the flag in README under a new "NATS backend" section. - Drop the stale duplicate redisRqueueQueueMetricsProvider bean from RqueueListenerAutoConfig (RqueueRedisListenerConfig already provides it with the correct RqueueRedisTemplate<String> injection). - Promote CoreUnitTest and TestUtils from rqueue-core/src/test to rqueue-test-util/src/main so cross-module tests in rqueue-redis and rqueue-web can use them. - Widen JetStreamMessageBroker constructor to public for sibling-package test access (regression caught by JetStreamMessageBrokerDelayThrowsTest). - Refresh nats-task.md tracker with the rqueue-web split, KV validator, worker registry, HttpUtils JDK migration, and the web-layer NATS dashboard gap as the next major follow-up. Assisted-By: Claude Code
1 parent ce7bfee commit 848e23d

42 files changed

Lines changed: 342 additions & 624 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

nats-task.md

Lines changed: 67 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -93,31 +93,86 @@ Snapshot of `nats-backend` branch progress and what's left to land. Kept here so
9393
- DAO impls: `RqueueStringDaoImpl`, `RqueueJobDaoImpl`, `RqueueMessageMetadataDaoImpl`, `RqueueQStatsDaoImpl`, `RqueueSystemConfigDaoImpl``rqueue-redis/src/main/java/com/github/sonus21/rqueue/redis/dao/`.
9494
- Metrics: `RedisRqueueQueueMetricsProvider``rqueue-redis/.../redis/metrics/`.
9595
- 5 `@Bean` factories from `RqueueListenerBaseConfig``RqueueRedisListenerConfig`: `rqueueRedisLongTemplate`, `rqueueRedisListenerContainerFactory`, `stringRqueueRedisTemplate`, `rqueueInternalPubSubChannel`, `rqueueStringDao`.
96-
- 5 more `@Bean` factories moved most recently: `scheduledMessageScheduler`, `processingMessageScheduler`, `rqueueWorkerRegistry`, `rqueueLockManager`, `rqueueQueueMetrics`.
97-
- 6 service impls (in flight, see Pending): `RqueueDashboardChartServiceImpl`, `RqueueJobServiceImpl`, `RqueueMessageMetadataServiceImpl`, `RqueueQDetailServiceImpl`, `RqueueSystemManagerServiceImpl`, `RqueueUtilityServiceImpl` — moved to `rqueue-redis/.../redis/web/service/impl/`.
96+
- 5 more `@Bean` factories: `scheduledMessageScheduler`, `processingMessageScheduler`, `rqueueWorkerRegistry`, `rqueueLockManager`, `rqueueQueueMetrics`.
97+
- 6 service impls: `RqueueDashboardChartServiceImpl`, `RqueueJobServiceImpl`, `RqueueMessageMetadataServiceImpl`, `RqueueQDetailServiceImpl`, `RqueueSystemManagerServiceImpl`, `RqueueUtilityServiceImpl``rqueue-redis/.../redis/web/service/impl/`.
98+
99+
### `rqueue-web` module extraction
100+
- New module `rqueue-web` registered in `settings.gradle`. `rqueue-spring-boot-starter`, `rqueue-spring`, `rqueue-redis`, and `rqueue-spring-common-test` declare `api project(":rqueue-web")` so the dashboard ships by default; consumers `<exclude>` it for headless workers.
101+
- Moved out of `rqueue-core/web/...` into `rqueue-web/web/...`: 5 controllers (`Base`, `BaseReactive`, `RqueueRest`, `RqueueView`, `ReactiveRqueueRest`, `ReactiveRqueueView`), `RqueueWebExceptionAdvice`, `RqueueViewControllerServiceImpl`, and the 6 web-only service interfaces (`RqueueDashboardChartService`, `RqueueJobMetricsAggregatorService`, `RqueueJobService`, `RqueueQDetailService`, `RqueueSystemManagerService`, `RqueueViewControllerService`). Stayed in core: `RqueueMessageMetadataService` and `RqueueUtilityService` interfaces (consumed by listener / endpoint manager).
102+
- Moved out of `rqueue-core/utils/pebble/`: 7 Pebble extension classes → `rqueue-web/utils/pebble/` (same package, no import changes).
103+
- Moved out of `rqueue-core/src/main/resources/`: `templates/rqueue/**`, `public/rqueue/**` (CSS, JS, vendor assets) → `rqueue-web/src/main/resources/`.
104+
- Moved out of `rqueue-core/src/test/`: `web/**` and `utils/pebble/**` test files → `rqueue-web/src/test/`. **Currently unbuildable** — see Pending.
105+
- Pebble view-resolver `@Bean`s extracted from `RqueueListenerBaseConfig` into a new `RqueueWebViewConfig` in `rqueue-web/web/config/`. Picked up via the existing `com.github.sonus21.rqueue.web` component scan.
106+
- `rqueue-core/build.gradle` dropped: `spring-webmvc`, `spring-webflux`, `jakarta.servlet-api`, `pebble-spring7`, `seruco/base62`, `hibernate-validator`, `org.glassfish:jakarta.el`. Added `reactor-core` directly (no longer comes via `spring-webflux`). `jakarta.validation-api` retained for DTO annotations.
107+
108+
### `HttpUtils` JDK-client migration
109+
- `HttpUtils.readUrl` rewritten to use `java.net.http.HttpClient` + Jackson; `org.springframework.web.client.RestTemplate` removed. `spring-web` dropped from `rqueue-core` deps.
110+
- `joinPath` retained unchanged; `RqueueWebConfig.getUrlPrefix` still calls it.
111+
112+
### Backend-agnostic worker registry
113+
- New SPI in `rqueue-core`: `WorkerRegistryStore` (7 narrow KV-shaped methods). `RqueueWorkerRegistryImpl` relocated from `rqueue-redis/redis/worker/` to `rqueue-core/worker/`; takes `(RqueueConfig, WorkerRegistryStore)` in the constructor. All heartbeat scheduling / view assembly logic backend-neutral.
114+
- `RedisWorkerRegistryStore` (rqueue-redis) wraps `RqueueRedisTemplate` over `set`/`get`/`mget`/hash ops.
115+
- `NatsWorkerRegistryStore` (rqueue-nats) wraps two JetStream KV buckets: `rqueue-workers` (TTL = `workerRegistry.workerTtl`), `rqueue-worker-heartbeats` (TTL = `workerRegistry.queueTtl`). Hash-of-strings emulated as flattened keys `<sanitizedQueueKey>__<sanitizedWorkerId>`. `refreshQueueTtl` is a no-op since NATS resets per-entry age on each write.
116+
- Wired in `RqueueRedisListenerConfig` and `RqueueNatsAutoConfig` under `@ConditionalOnMissingBean`.
117+
118+
### NATS KV bucket lifecycle (`rqueue.nats.autoCreateKvBuckets`)
119+
- New `com.github.sonus21.rqueue.nats.kv.NatsKvBuckets` constants class — single source of truth for the 6 bucket names (`QUEUE_CONFIG`, `JOBS`, `LOCKS`, `MESSAGE_METADATA`, `WORKERS`, `WORKER_HEARTBEATS`) + `ALL_BUCKETS` list. Every store / dao now references this constant instead of a private string.
120+
- New `com.github.sonus21.rqueue.nats.kv.NatsKvBucketValidator` — config-source-agnostic class; constructor takes `(Connection, boolean autoCreate)`. Static `validate(Connection, boolean)` walks `ALL_BUCKETS` via `kvm.getStatus(name)` and aborts with `IllegalStateException` listing missing buckets. Implements `InitializingBean` so the bean form re-runs the same check.
121+
- New `rqueue.nats.autoCreateKvBuckets` field on `RqueueNatsProperties` (default `true`). `rqueue-nats` itself never reads `rqueue.nats.*` keys directly — the property flows in only through the auto-config.
122+
- Two enforcement layers in `RqueueNatsAutoConfig`:
123+
1. Inline call to `NatsKvBucketValidator.validate(connection, props.isAutoCreateKvBuckets())` inside the `natsConnection` `@Bean` factory, so validation completes during `Connection` bean creation (strictly before any other NATS bean can inject the connection).
124+
2. `@Bean public NatsKvBucketValidator natsKvBucketValidator(...)` declared from `RqueueNatsProperties`. Five NATS components (`NatsRqueueSystemConfigDao`, `NatsRqueueJobDao`, `NatsRqueueLockManager`, `NatsRqueueMessageMetadataService`, `NatsWorkerRegistryStore`) plus the `WorkerRegistryStore` `@Bean` factory carry `@DependsOn("natsKvBucketValidator")` so they wait on it even when the inline path is bypassed.
125+
126+
### Web-layer infrastructure for capability-aware errors
127+
- `BackendCapabilityException` (in `rqueue-core/exception/`) — carries `{backend, operation, reason}`. Mapped to HTTP 501 with structured JSON body by `RqueueWebExceptionAdvice` (in `rqueue-web/web/controller/`, scoped `@RestControllerAdvice(basePackageClasses = ...)`). No callers yet — landed as scaffolding for the upcoming web-service repository-interface refactor.
128+
129+
### README — NATS backend section
130+
- New "NATS backend" section in `README.md` covering: the 6 KV buckets (table with name, purpose, TTL behaviour, code link), how buckets are configured (lazy / immutable `ttl` / connection wiring), pre-create commands for restricted JetStream accounts, the `rqueue.nats.autoCreateKvBuckets=false` flag and its two-layer enforcement, and a recreate-with-new-TTL recipe.
98131

99132
### CI & PR
100133
- Branch `nats-backend` pushed to `origin`. ~50 commits, all carry `Assisted-By: Claude Code` only (no `Co-Authored-By:`). `CLAUDE.md` documents the rule.
101134

102135
## Pending items
103136

104-
### In flightfinish service impl move (slice B of `@Conditional` cleanup)
137+
### Build greenthree test-compile failures across the tree
105138

106-
The 6 `*ServiceImpl` files have moved to `rqueue-redis`. Their unit tests have been moved alongside. **Compilation in `rqueue-redis:test` is failing** because the moved tests depend on `CoreUnitTest` (annotation) and `QueueStatisticsTest` (fixture data) which still live in `rqueue-core/src/test`. Two paths:
139+
Same root cause for two of three (cross-module visibility of test fixtures). Pick **option 1** below: promote the offenders to `rqueue-test-util/src/main`.
107140

108-
1. Promote `CoreUnitTest` + `QueueStatisticsTest` to `rqueue-test-util/src/main` so they're visible across modules. Smallest change.
109-
2. Add Gradle `java-test-fixtures` to `rqueue-core` and pull from `rqueue-redis` via `testFixtures(project(":rqueue-core"))`. More canonical Gradle but bigger setup.
141+
1. **`rqueue-redis:compileTestJava`** — moved tests reference `CoreUnitTest` (annotation) and `QueueStatisticsTest` (fixture data) still living in `rqueue-core/src/test`.
142+
2. **`rqueue-web:compileTestJava`**`DateTimeFunctionTest`, `RqueueTaskMetricsAggregatorServiceTest`, `RqueuePebbleExtensionTest` reference `CoreUnitTest` and `TestUtils.createQueueDetail`, both still in `rqueue-core/src/test`.
143+
3. **`rqueue-nats:compileTestJava`**`JetStreamMessageBrokerDelayThrowsTest:36` calls `new JetStreamMessageBroker(Connection, JetStream, JetStreamManagement, RqueueNatsConfig, ObjectMapper)` from outside the broker's package. The constructor is package-private (regression from a recent visibility tighten). Fix: widen the constructor to `public`, or use the existing `JetStreamMessageBroker.builder()` API in the test.
110144

111-
Pick **option 1** for simplicity. Move:
112-
- `rqueue-core/src/test/java/com/github/sonus21/rqueue/CoreUnitTest.java``rqueue-test-util/src/main/java/com/github/sonus21/rqueue/CoreUnitTest.java`
113-
- `rqueue-core/src/test/java/com/github/sonus21/rqueue/models/db/QueueStatisticsTest.java` → split into a fixture helper in `rqueue-test-util` and a thin test that re-exercises it in core (or just keep both copies during transition).
145+
Plan for items 1 + 2:
146+
- Move `rqueue-core/src/test/java/com/github/sonus21/rqueue/CoreUnitTest.java``rqueue-test-util/src/main/java/com/github/sonus21/rqueue/CoreUnitTest.java`.
147+
- Move `rqueue-core/src/test/java/com/github/sonus21/rqueue/utils/TestUtils.java``rqueue-test-util/src/main/java/com/github/sonus21/rqueue/utils/TestUtils.java`.
148+
- (Optional) `QueueStatisticsTest` fixture data — promote helpers if the moved Redis tests still need them.
149+
- All consumer modules already pull `rqueue-test-util` as `testImplementation`, so no build wiring needed.
114150

115-
Then re-run `./gradlew :rqueue-core:test :rqueue-redis:test :rqueue-nats:test -DincludeTags=unit` and `./gradlew :rqueue-spring-boot-starter:test --tests NatsBackendEndToEndIT`.
151+
Then re-run:
152+
```
153+
./gradlew :rqueue-core:test :rqueue-redis:test :rqueue-web:test :rqueue-nats:test -DincludeTags=unit
154+
./gradlew :rqueue-spring-boot-starter:test --tests "com.github.sonus21.rqueue.spring.boot.integration.NatsBackendEndToEndIT"
155+
```
156+
157+
### Web-layer NATS dashboard gap (new follow-up)
158+
159+
All 4 controllers and the 5 web service impls (`RqueueDashboardChartService*`, `RqueueQDetailService*`, `RqueueJobService*`, `RqueueSystemManagerService*`, `RqueueUtilityService*`) are still gated `@Conditional(RedisBackendCondition)`. On NATS the dashboard reports broker-derived sizes only; no charts, no message browse, no admin ops. Plan to fix:
160+
161+
1. Introduce repository interfaces in `rqueue-core/repository/` for the few storage primitives the web services share (queue browsing, time-series counters, atomic move). Web service impls move into core / `rqueue-web` and depend only on the repos.
162+
2. Redis impls of the repos stay in `rqueue-redis`; NATS impls go in `rqueue-nats` and throw `BackendCapabilityException("nats", "operation", "reason")` for primitives JetStream can't model (positional message moves, time-bucket charts).
163+
3. Drop `@Conditional(RedisBackendCondition)` from controllers; the advice already maps the exception to HTTP 501 with a structured body.
164+
4. Extend the existing `Capabilities` record (`MessageBroker.capabilities()`) with dashboard-op flags so the front-end can hide unsupported panels instead of relying on 501s. Expose `GET /rqueue/api/capabilities`.
165+
166+
Order of operations: easiest first — `RqueueSystemManagerService` (already mostly goes through `RqueueSystemConfigDao`), then `RqueueJobService`, `RqueueViewControllerService`, then `RqueueQDetailService` (needs new `MessageBrowsingRepository`), then `RqueueDashboardChartService` and `RqueueUtilityService.move/enqueue` last (these throw on NATS).
167+
168+
### Spring Boot configuration metadata
169+
170+
`spring-configuration-metadata.json` has no entry for `rqueue.nats.autoCreateKvBuckets`. IDE autocomplete won't show it. Easy follow-up: add `spring-boot-configuration-processor` to the starter's annotation processors if not already wired.
116171

117172
### Other open follow-ups
118173

119-
- **`RqueueStringDao` interface** itself — analysis recommended keeping it Redis-only (don't split into smaller cross-backend interfaces). It already has zero NATS-path consumers; just document it as Redis-internal in javadoc.
120-
- **`RqueueMessageMetadataDao`, `RqueueQStatsDao`** — no NATS impls needed; all consumers are Redis-only gated. Verified, no action.
174+
- **`RqueueStringDao` interface** — keep Redis-only; document as Redis-internal in javadoc.
175+
- **`RqueueMessageMetadataDao`, `RqueueQStatsDao`** — no NATS impls needed; all consumers are Redis-only gated. Re-verify in light of the web-layer refactor above.
121176
- **Reactive listener container** — only enqueue side is reactive in v1. Phase 5 territory.
122177
- **Delayed/scheduled/cron messages on NATS** — throws `UnsupportedOperationException`. Out of scope for v1.
123178
- **Cross-queue `priorityGroup` weighting on NATS** — boot WARN, not honored. Acceptable for v1.

0 commit comments

Comments
 (0)