Package com.tnsai.idempotency
Class PostgresIdempotencyStore
java.lang.Object
com.tnsai.idempotency.PostgresIdempotencyStore
- All Implemented Interfaces:
IdempotencyStore
Durable
IdempotencyStore backed by a JDBC database
(issue #91). Targets PostgreSQL but works with any database whose
dialect handles TIMESTAMP WITH TIME ZONE, TEXT, and
standard SQL UPDATE / INSERT: H2 in PostgreSQL mode, CockroachDB,
any modern Postgres-compatible engine.
Strong durability: an entry stored here survives every kind of
process churn that InMemoryIdempotencyStore would lose. The
trade-off is one round-trip per get / put -- fine for moderate
traffic, prefer RedisIdempotencyStore for hot loops.
Schema
The store expects a table namedidempotency_entries (override
via the table-name constructor):
CREATE TABLE idempotency_entries (
idempotency_key VARCHAR(512) PRIMARY KEY,
entry_json TEXT NOT NULL,
expires_at TIMESTAMP WITH TIME ZONE NOT NULL
);
CREATE INDEX idx_idempotency_expires_at ON idempotency_entries (expires_at);
Note that the primary-key column is idempotency_key rather
than the bare word key -- key is reserved in many SQL
dialects (H2 strictly, MySQL contextually) and quoting it would force
dialect-specific identifier escaping.
createTableIfMissing() runs the equivalent DDL for
tests / quick-starts. Production deployments typically run schema
migration via Flyway / Liquibase and never call that method.
Expiry
Lazy: everyget(String) filters on
expires_at > now(). Expired rows linger in storage until the
operator runs purgeExpired() (manual or scheduled) -- this
keeps the hot path single-row and lock-free at the cost of letting
the table grow until purged.- Since:
- 0.10.0 (issue #91 closeout)
-
Field Summary
Fields -
Constructor Summary
ConstructorsConstructorDescriptionPostgresIdempotencyStore(DataSource dataSource) PostgresIdempotencyStore(DataSource dataSource, String table) -
Method Summary
Modifier and TypeMethodDescriptionvoidIdempotently create the schema if it doesn't exist.Look up a cached entry.voidinvalidate(String key) Remove the entry forkeyif any.intDelete rows whoseexpires_atis already in the past.voidput(String key, IdempotencyEntry entry, Duration ttl) Store an entry underkeywith the given TTL.intsize()Approximate entry count.
-
Field Details
-
DEFAULT_TABLE
Default table name. Override via the two-arg constructor.- See Also:
-
-
Constructor Details
-
PostgresIdempotencyStore
-
PostgresIdempotencyStore
-
-
Method Details
-
get
Description copied from interface:IdempotencyStoreLook up a cached entry. Returns empty when no entry exists OR when the entry's TTL has elapsed.- Specified by:
getin interfaceIdempotencyStore
-
put
Description copied from interface:IdempotencyStoreStore an entry underkeywith the given TTL. SubsequentIdempotencyStore.get(java.lang.String)calls within the TTL window return this entry. Overwrites any prior entry for the same key.- Specified by:
putin interfaceIdempotencyStore
-
invalidate
Description copied from interface:IdempotencyStoreRemove the entry forkeyif any. No-op if the key is not present.- Specified by:
invalidatein interfaceIdempotencyStore
-
size
public int size()Description copied from interface:IdempotencyStoreApproximate entry count. Intended for monitoring / smoke-testing ("cache has grown to 10M entries, check your TTL"). Implementations may return an eventually-consistent snapshot.- Specified by:
sizein interfaceIdempotencyStore
-
purgeExpired
public int purgeExpired()Delete rows whoseexpires_atis already in the past. Returns the number of rows removed. Run periodically (cron, a scheduled framework job) to keep the table from growing unboundedly. -
createTableIfMissing
public void createTableIfMissing()Idempotently create the schema if it doesn't exist. Useful for tests and quick-start deployments; production environments typically run schema migration via Flyway / Liquibase and skip this method.
-