Skip to content

fix: the api jar did not start, and nothing in the build noticed - #65

Merged
TheMeinerLP merged 1 commit into
mainfrom
fix/scheduled-index-refresh
Aug 17, 2026
Merged

fix: the api jar did not start, and nothing in the build noticed#65
TheMeinerLP merged 1 commit into
mainfrom
fix/scheduled-index-refresh

Conversation

@TheMeinerLP

Copy link
Copy Markdown
Contributor

Two failures, one release. Both were found by the cluster rather than by the build, which is the part worth fixing.

The api would not start

TenantGroupIndexLoader declared @Scheduled(fixedDelay = "60s"). Micronaut's converter rejects that spelling, and because the scheduled-method processor runs during context start(), it did not fail one bean — it failed the application:

SchedulerConfigurationException: Invalid @Scheduled definition for method: void refresh()
  - Reason: Invalid fixed delay definition: 60s

0.9.0 was published, rolled out, and the single api replica went into CrashLoopBackOff — the whole API was down for about eight minutes, until the platform chart was rolled back to 0.8.0 (Kubernetes-FLUX#177).

It now schedules through TaskScheduler with a typed Duration. A constant cannot be spelled wrong, and if it could, the compiler would say so instead of the kubelet.

Nothing in the build could have caught it

This took two attempts to establish honestly, and the first attempt is worth reporting.

I added a test that boots a real Micronaut context with an embedded server, on the theory that it would have caught this. Then I reintroduced the bug — faithfully, public final void refresh() and all — and the test passed. The difference is not the environment. It is the shadowed jar: on the plain test classpath "60s" converts fine, and no @MicronautTest can see the failure.

So :api:startupSmokeTest boots the shadowed jar and fails if the context does not start, wired into check. Verified in both directions — green on the fix, red on the reintroduced bug, with the real error in the failure message:

> the shadowed api jar did not start -- this is what reaches the cluster:
  ERROR io.micronaut.runtime.Micronaut - Error starting Micronaut server: Invalid fixed delay definition: 60s

ApplicationStartupTest stays for what it does catch (unsatisfiable beans, factories that throw, properties that will not bind), with its Javadoc corrected to say plainly that this is not it.

The operator could not read Rook's own resource

Separately, and not introduced by 0.9.0: CephObjectStoreUserStatus modelled only phase, while Rook had added status.info and status.observedGeneration. TenantReconciler reads the existing user before touching it, so every reconciliation of every tenant threw UnrecognizedPropertyException: Unrecognized field "info" and exhausted its retries.

Nothing looked broken — a tenant only reconciles when something changes — until this release needed one. Then the tenant silently never got its application instance, which is how it was found.

All four Rook model types now ignore unmodelled fields. The fix is deliberately not to add info: that is a race against another project's roadmap. A model of somebody else's resource must not fail on a field it never reads. The test uses the verbatim JSON from the live cluster.

Verification

:api:check passes, including the new startupSmokeTest
:operator:test passes, incl. two new Rook tolerance tests (red before the fix)
spotlessCheck clean

After merge

Release 0.9.1, then bring the apus-platform pin in Kubernetes-FLUX back from =0.8.0 to =0.9.1. The comment at that pin says so and names the condition.

🤖 Generated with Claude Code

https://claude.ai/code/session_019Bff5mpWkUnZA77jys8DiR

Two failures, one release. Both were found by the cluster rather than by the
build, which is the part worth fixing.

## The api would not start

TenantGroupIndexLoader declared @scheduled(fixedDelay = "60s"). Micronaut's
converter rejects that spelling, and because the scheduled-method processor runs
during context start, it did not fail one bean -- it failed the application. The
single api replica went into CrashLoopBackOff and took the whole API down for
about eight minutes, until the platform chart was rolled back to 0.8.0.

It now schedules through TaskScheduler with a typed Duration. A constant cannot
be spelled wrong, and if it could, the compiler would say so instead of the
kubelet.

## Nothing in the build could have caught it

This took two attempts to establish honestly. A test that boots a real Micronaut
context with an embedded server passed with the bug faithfully reintroduced -- so
that "guard" was worthless. The difference is not the environment, it is the
shadowed jar: on the plain test classpath "60s" converts fine.

So :api:startupSmokeTest boots the shadowed jar and fails if the context does not
start, and it is wired into check. Verified in both directions -- green on the
fix, red on the reintroduced bug, with the real error in the failure message.
ApplicationStartupTest stays for what it does catch, with its Javadoc corrected
to say plainly that this is not it.

## The operator could not read Rook's own resource

Separately, and not new: CephObjectStoreUserStatus modelled only `phase`, while
Rook had added `status.info` and `status.observedGeneration`. TenantReconciler
reads the existing user before touching it, so every reconciliation of every
tenant threw UnrecognizedPropertyException and exhausted its retries. Nothing
looked broken -- a tenant only reconciles when something changes -- until this
release needed one, and then the tenant silently never got its application
instance.

All four Rook model types now ignore unmodelled fields. The fix is deliberately
not "add info": that is a race against another project's roadmap. A model of
somebody else's resource must not fail on a field it never reads. The test uses
the verbatim JSON from the live cluster.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Bff5mpWkUnZA77jys8DiR
@github-actions

Copy link
Copy Markdown
Contributor

Test results

675 tests   675 ✅  2m 3s ⏱️
 94 suites    0 💤
 94 files      0 ❌

Results for commit 3bae1f6.

@TheMeinerLP
TheMeinerLP merged commit 633f308 into main Aug 17, 2026
8 checks passed
@TheMeinerLP
TheMeinerLP deleted the fix/scheduled-index-refresh branch August 17, 2026 07:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant