Skip to content

feat: OutboxProcessorScheduler — configurability, reliability, SmartLifecycle (KOJAK-62)#14

Open
endrju19 wants to merge 8 commits intofeat102from
process-scheduler
Open

feat: OutboxProcessorScheduler — configurability, reliability, SmartLifecycle (KOJAK-62)#14
endrju19 wants to merge 8 commits intofeat102from
process-scheduler

Conversation

@endrju19
Copy link
Collaborator

Summary

Apply the same production-ready pattern from KOJAK-56 (OutboxPurger v2) to OutboxScheduler and OutboxProcessorScheduler:

  • Fix silent death bug: OutboxScheduler.tick() had no try/catch — ScheduledExecutorService silently stops on uncaught exception. Added catch(Exception) with SLF4J error logging.
  • Add lifecycle guards: AtomicBoolean running flag prevents double-start, check(!scheduler.isShutdown) prevents restart-after-stop with a clear error message, isRunning() method.
  • Extract config value object: OutboxSchedulerConfig data class with require() validation in init block (make illegal states unrepresentable).
  • Migrate to SmartLifecycle: Replace SmartInitializingSingleton + DisposableBean with SmartLifecycle. Phase ordering: processor (MAX_VALUE - 2048) starts before purger (MAX_VALUE - 1024) and stops after it. stop(callback) with try-finally.
  • Spring properties binding: OutboxProcessorProperties with @ConfigurationProperties(prefix = "okapi.processor"), @Validated, @field:Min(1). @ConditionalOnProperty for enabled toggle.
  • Database index: (status, created_at) composite index for claimPending() query performance (Postgres + MySQL).
  • Configuration metadata: Processor properties added to spring-configuration-metadata.json.

Configuration

okapi:
  processor:
    enabled: true       # default
    interval-ms: 1000   # default
    batch-size: 10      # default

Commits (8)

Commit Scope
feat(core): add OutboxSchedulerConfig value object with validation okapi-core
feat(core): rewrite OutboxScheduler with error handling, guards, logging okapi-core
feat(spring): add OutboxProcessorProperties okapi-spring-boot
feat(spring): migrate OutboxProcessorScheduler to SmartLifecycle okapi-spring-boot
feat(spring): bind OutboxProcessorProperties in OutboxAutoConfiguration okapi-spring-boot
test(spring): add OutboxProcessorAutoConfigurationTest okapi-spring-boot
feat(db): add index (status, created_at) for processor claimPending query okapi-postgres, okapi-mysql
feat(spring): add processor properties to spring-configuration-metadata.json okapi-spring-boot

Test plan

  • OutboxSchedulerConfigTest — 6 tests (defaults, custom values, validation)
  • OutboxSchedulerTest — 7 tests (batchSize forwarding, error recovery, double-start, isRunning transitions, restart-after-stop, transactionRunner wrapping, no transactionRunner)
  • OutboxProcessorAutoConfigurationTest — 7 tests (bean creation, disabled toggle, properties binding, defaults, SmartLifecycle isRunning, validation, stop callback)
  • All existing tests pass (purger, publisher, E2E)
  • ./gradlew clean check — BUILD SUCCESSFUL
  • ./gradlew ktlintCheck — clean

Related

Add try/catch in tick() to prevent silent scheduler death on exception.
Add AtomicBoolean running guard and isShutdown restart check.
Accept OutboxSchedulerConfig instead of raw parameters.
Add SLF4J logging: INFO start/stop, DEBUG per tick, ERROR on failure.
Add isRunning() method.
Replace SmartInitializingSingleton + DisposableBean with SmartLifecycle.
Add stop(callback) with try-finally to prevent Spring shutdown hang.
Add phase ordering: processor (MAX_VALUE-2048) starts before purger
(MAX_VALUE-1024) and stops after it.
Accept OutboxSchedulerConfig instead of raw parameters.
Add @EnableConfigurationProperties for OutboxProcessorProperties.
Add @ConditionalOnProperty for okapi.processor.enabled toggle.
Map properties to OutboxSchedulerConfig in bean factory.
Test: bean creation, disabled toggle, properties binding, defaults,
SmartLifecycle isRunning, validation, stop callback safety.
…uery

Without this index, claimPending() does a full table scan on
SELECT ... WHERE status='PENDING' ORDER BY created_at.
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