Nothing to declare: with idempotency-jdbc on the classpath and a DataSource in the
context, the store is built for you, complete with the TransactionAwareConnectionResolver
that joined completion needs.
Maven
<dependency>
<groupId>io.github.josipmusa</groupId>
<artifactId>idempotency-jdbc</artifactId>
<version>0.5.0</version>
</dependency>Gradle
implementation 'io.github.josipmusa:idempotency-jdbc:0.5.0'Tested on PostgreSQL 16, MySQL 8.0 and H2 2.x.
The table
The table is another matter. idempotency.jdbc.initialize-schema follows the convention
Spring Boot uses for Session and Quartz:
idempotency:
jdbc:
initialize-schema: embedded # embedded (default) | always | neverPostgreSQL
Point Flyway, Liquibase or your own migration at idempotency-schema-postgresql.sql, shipped
inside the idempotency-jdbc jar.
Or set initialize-schema: always if you would rather the store created the table itself.
MySQL
Point Flyway, Liquibase or your own migration at idempotency-schema-mysql.sql, shipped
inside the idempotency-jdbc jar.
Or set initialize-schema: always if you would rather the store created the table itself.
H2
Nothing to do. embedded creates the table on an embedded database and only there, so a
development H2 and the store contract’s own test database both work untouched.
If the table still cannot be queried once the context has started, the starter logs a warning saying so, rather than leaving the first keyed request to fail with a 500.
Constructing it by hand
Constructing the store by hand still works, and there the initSchema flag is yours:
@Bean
public IdempotencyStore idempotencyStore(DataSource dataSource) {
return new JdbcIdempotencyStore(dataSource, false, new TransactionAwareConnectionResolver(dataSource));
}A store bean you declare always wins, so this is also how you point the store at a second
DataSource - a separate database for idempotency records - rather than the application’s
primary one.
Joined completion
The JDBC store is the only shipped store that supports it. The
TransactionAwareConnectionResolver runs the joined completion on the transaction-bound
connection and everything else, an autonomous completion included, on a connection of its
own, which is what lets the record commit with your business writes while the lease work
stays outside your transaction.
Until that transaction commits, it holds the record’s row. A duplicate that arrives meanwhile
does not wait for the commit beyond its own waitTimeout: it is told the record is in flight,
with the holder’s remaining lease as retryAfter. Query timeouts are whole seconds, so the
wait can overshoot by up to a second, or two on H2.