Skip to content

Release 3.0.0

Latest

Choose a tag to compare

@arun0009 arun0009 released this 02 Jun 06:58
· 10 commits to main since this release
12f2ee2

🎉 Idempotent 3.0.0 — Run Once. Same Result on Every Retry.

3.0.0 is a major breaking release: cleaner Spring-native integration, a consistent store contract across every backend, and stronger, more predictable runtime semantics.

Full upgrade details live in docs/MIGRATION.md. The highlights below cover everything that can change your build.

🚨 Biggest Change: Redis Integration Reworked

Redis now uses Spring-native wiring end-to-end instead of managing its own connections.

  • Uses your application’s RedisConnectionFactory (Lettuce, Jedis, or any Spring Data Redis driver)
  • Configure Redis via standard spring.data.redis.*
  • Redis module config is now just idempotent.redis.enabled (default true)
  • Removed all idempotent-specific Redis connection properties:
    • idempotent.redis.standalone.*, cluster.*, sentinel.*, auth.*, ssl.*
  • Redis serialization now uses the shared IdempotentPayloadCodec (same Jackson setup as core), replacing enableUnsafeDefaultTyping()

Store Contract — Now Explicit and Aligned

IdempotentStore has a single, documented contract across all backends:

  • store(...) is a strict insert — an existing key throws IdempotentKeyConflictException
  • update(...) is a no-op when the key is missing (never resurrects a removed/expired entry)
  • Stores implement loadValue(key, returnType) as a raw read
  • Default getValue(...) in core applies expiry + best-effort lazy cleanup, so every backend behaves the same
  • Expiry is evaluated against wall-clock time (expiresAt); keep nodes NTP-synced and size TTLs above your slowest op plus expected skew

Runtime Semantics

  • IdempotentAspect now delegates the full state machine to IdempotentService (single source of truth)
  • Operation exceptions are rethrown as the original domain exception (no longer wrapped in IdempotentException); the in-progress entry is still cleaned up first
  • Successful null / void results are now cached as COMPLETED (duplicate calls short-circuit instead of re-executing)
  • Non-2xx ResponseEntity results are not cached, so the caller can retry
  • New typed overloads: IdempotentService.execute(key, returnType, operation, ttl) — lets RDS/DynamoDB deserialize into the target type without relying on Jackson polymorphic @class metadata
  • @Idempotent(duration = ...) accepts both ISO-8601 (PT5M) and Spring short forms (5m, 30s, 100ms)
  • @Idempotent logs a warning (once per method) when the resolved key is empty, instead of silently skipping idempotency

Breaking Changes by Area

Core / @Idempotent

  • Removed ttlInSeconds — use duration only
  • IdempotentAspect is only registered when Spring AOP is on the classpath (add spring-boot-starter-aop); without it @Idempotent won’t intercept. IdempotentService is always available.
  • IdempotentAspect constructor changed: (idempotentStore, properties)(idempotentService, properties)
  • IdempotentPayloadCodecException now extends IdempotentException
  • IdempotentStore.Value.status uses enum IN_PROGRESS / COMPLETED
  • IdempotentStore.Value.expiresAt is now Instant
  • Retry property renamed: idempotent.inprogress.retry.initial.interval-millisidempotent.inprogress.retry.initial.interval (Duration, e.g. 100ms / PT0.1S)
  • Include only one backend module on the classpath, or provide your own IdempotentStore bean

Redis

  • See “Biggest Change” above

DynamoDB

  • Removed idempotent.dynamodb.use-local — use idempotent.dynamodb.endpoint for local/test
  • New idempotent.dynamodb.enabled (default true)
  • New idempotent.dynamodb.ttl-enabled (default true; set false if TTL is already configured on your table)
  • TTL attribute is expiresAtEpochSeconds (epoch seconds); removed legacy expirationTimeInMilliSeconds
  • idempotent.aws.region / access-key / access-secret are now optional (omit to use the default AWS credential chain)
  • idempotent.dynamodb.endpoint is optional (null when unset)

NATS

  • Property rename: idempotent.nats.enableidempotent.nats.enabled
  • Exception rename: NatsIdempotentExceptionsNatsIdempotentException

RDS

  • Schema column rename: expiration_time_millisexpires_at (BIGINT, epoch milliseconds) — migrate/recreate your table
  • New idempotent.rds.enabled (default true)
  • New idempotent.rds.cleanup.enabled (default true)
  • idempotent.rds.cleanup.fixed-delay is now a Duration (default PT1M)

Serialization

  • Removed deprecated module-specific Jackson customizers:
    • IdempotentJacksonJsonBuilderCustomizer (redis/nats)
    • RdsJacksonJsonBuilderCustomizer (rds)
    • Use IdempotentJsonMapperCustomizer from idempotent-core

Upgrade Checklist

  1. Add spring-boot-starter-aop if you use @Idempotent.
  2. Redis: move connection settings to spring.data.redis.*; remove old idempotent.redis.* connection properties (keep idempotent.redis.enabled).
  3. DynamoDB: drop use-local; review enabled / ttl-enabled / endpoint; ensure your TTL attribute is expiresAtEpochSeconds.
  4. RDS: rename the schema column to expires_at; review the new enabled / cleanup.* flags.
  5. NATS: update the renamed property and exception references.
  6. Replace ttlInSeconds with duration; update the retry interval property to a Duration.
  7. If you have a custom store, implement loadValue(...) and rely on the default getValue(...) for expiry.
  8. Re-run integration tests against your backend (Redis / DynamoDB / NATS / RDS).

What's Changed

🔨 Dependency Upgrades

  • Bump software.amazon.awssdk:dynamodb-enhanced from 2.42.32 to 2.42.34 by @dependabot[bot] in #101
  • Bump com.uber.nullaway:nullaway from 0.13.2 to 0.13.3 by @dependabot[bot] in #100
  • Bump softprops/action-gh-release from 2 to 3 by @dependabot[bot] in #98
  • Bump software.amazon.awssdk:dynamodb-enhanced from 2.42.34 to 2.42.35 by @dependabot[bot] in #102
  • Bump software.amazon.awssdk:dynamodb-enhanced from 2.42.35 to 2.42.40 by @dependabot[bot] in #108
  • Bump spring-boot.version from 4.0.5 to 4.0.6 by @dependabot[bot] in #107
  • Bump com.uber.nullaway:nullaway from 0.13.3 to 0.13.4 by @dependabot[bot] in #104
  • Bump software.amazon.awssdk:dynamodb-enhanced from 2.42.40 to 2.43.0 by @dependabot[bot] in #109
  • Bump software.amazon.awssdk:dynamodb-enhanced from 2.43.0 to 2.43.2 by @dependabot[bot] in #112
  • Bump org.jreleaser:jreleaser-maven-plugin from 1.23.0 to 1.24.0 by @dependabot[bot] in #111
  • Bump software.amazon.awssdk:dynamodb-enhanced from 2.43.2 to 2.44.1 by @dependabot[bot] in #114
  • Bump JetBrains/qodana-action from 2025.3 to 2026.1 by @dependabot[bot] in #113
  • Bump io.nats:jnats from 2.25.2 to 2.25.3 by @dependabot[bot] in #117
  • Bump software.amazon.awssdk:dynamodb-enhanced from 2.44.1 to 2.44.3 by @dependabot[bot] in #116
  • Bump software.amazon.awssdk:dynamodb-enhanced from 2.44.3 to 2.44.4 by @dependabot[bot] in #118
  • Bump software.amazon.awssdk:dynamodb-enhanced from 2.44.4 to 2.44.6 by @dependabot[bot] in #121
  • Bump com.diffplug.spotless:spotless-maven-plugin from 3.4.0 to 3.5.0 by @dependabot[bot] in #120
  • Bump software.amazon.awssdk:dynamodb-enhanced from 2.44.6 to 2.44.8 by @dependabot[bot] in #123
  • Bump com.diffplug.spotless:spotless-maven-plugin from 3.5.0 to 3.5.1 by @dependabot[bot] in #122
  • Bump software.amazon.awssdk:dynamodb-enhanced from 2.44.8 to 2.44.9 by @dependabot[bot] in #124
  • Bump software.amazon.awssdk:dynamodb-enhanced from 2.44.9 to 2.44.10 by @dependabot[bot] in #126
  • Bump software.amazon.awssdk:dynamodb-enhanced from 2.44.10 to 2.44.13 by @dependabot[bot] in #130
  • Bump software.amazon.awssdk:dynamodb-enhanced from 2.44.13 to 2.45.1 by @dependabot[bot] in #133
  • Bump com.diffplug.spotless:spotless-maven-plugin from 3.5.1 to 3.6.0 by @dependabot[bot] in #131

Other Changes

  • Prepare for next release by @github-actions[bot] in #97
  • 3.0: Redis decoupling and store auto-config cleanup by @arun0009 in #127

Full Changelog: v2.4.1...v3.0.0