Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

27 Commits

Firefly Framework - Cache - Postgres

CILicenseJavaSpring Boot

Durable PostgreSQL/R2DBC cache provider adapter for the Firefly Framework cache abstraction — a fully reactive, SQL-backed CacheAdapter with TTL expiry, atomic put-if-absent, and prefix eviction.


Table of Contents

Overview

fireflyframework-cache-postgres is one of the pluggable cache provider adapters for the Firefly Framework cache abstraction. It implements the CacheAdapter port defined in fireflyframework-cache (the cache core) using PostgreSQL as a persistent, distributed cache backend over fully reactive R2DBC.

The cache core ships Caffeine as the in-process default. Every other backend — Redis, Hazelcast, JCache and PostgreSQL — lives in its own adapter module, so an application only pulls in the dependency it actually uses. This module contributes a PostgresProvider (discovered through the CacheProviderFactoryServiceLoader SPI) and a PostgresCacheAutoConfiguration. Once on the classpath with firefly.cache.postgres.enabled=true, the cache core makes the POSTGRES cache type available to FireflyCacheManager and selects it when firefly.cache.default-cache-type=POSTGRES (or via AUTO).

Use this adapter when you want a durable, SQL-backed cache rather than an in-memory or Redis cache — for example, when entries must survive process restarts, when cache contents should be queryable and auditable through ordinary SQL, or when you would rather reuse your existing PostgreSQL infrastructure than operate a separate cache cluster. Cache entries live in a single relational table keyed by (cache_name, cache_key), with values serialized to JSON and stored in a BYTEA column; time-to-live is enforced through an expires_at timestamp checked lazily on every read. Because the adapter is built on R2DBC and Project Reactor, every operation is non-blocking and integrates cleanly with Spring WebFlux and the rest of the Firefly Framework.

Where it sits in the framework

ModuleRole
fireflyframework-cacheCache abstraction + SPI + Caffeine default (CacheAdapter, CacheType, CacheProviderFactory, FireflyCacheManager)
fireflyframework-cache-redisRedis provider adapter
fireflyframework-cache-hazelcastHazelcast provider adapter
fireflyframework-cache-jcacheJCache (JSR-107) provider adapter
fireflyframework-cache-postgresPostgreSQL / R2DBC provider adapter (this module)

Features

  • Full CacheAdapter implementation (PostgresCacheAdapter) backed by PostgreSQL R2DBC.
  • Reactive, non-blocking operations with per-operation connection lifecycle via Mono.usingWhen.
  • TTL support with lazy expiry on read (expires_at IS NULL OR expires_at > NOW()) and write-time expires_at = NOW() + ttl.
  • JSON serialization to a BYTEA column through the framework CacheSerializer (JsonCacheSerializer).
  • Atomic putIfAbsent using INSERT ... ON CONFLICT DO NOTHING; upserts via ON CONFLICT DO UPDATE.
  • Native prefix eviction (evictByPrefix) via DELETE ... WHERE cache_key LIKE prefix || '%'.
  • Per-cache key namespacing (<keyPrefix>:<cacheName>:<key>) so one table hosts many named caches.
  • Pooled connections via r2dbc-pool with configurable min/max pool sizing.
  • Optional automatic schema creation (cache table + partial expiry index) on startup.
  • Connection health checks via Connection.validate(ValidationDepth.REMOTE), surfaced as CacheHealth.
  • In-process statistics (requests, hits, misses, puts, evictions, entry count) as CacheStats.
  • ServiceLoader SPI registration (PostgresProvider, CacheType.POSTGRES, priority 15) plus Spring Boot auto-configuration.

Requirements

  • Java 21+ (Java 25 recommended)
  • Spring Boot 3.x
  • Maven 3.9+
  • PostgreSQL 12+ reachable over R2DBC
  • fireflyframework-cache on the classpath (pulled in transitively as a dependency)

Installation

Add the adapter to your application. The version is managed by the Firefly Framework parent POM / BOM, so you normally omit <version>:

<dependency>
<groupId>org.fireflyframework</groupId>
<artifactId>fireflyframework-cache-postgres</artifactId>
</dependency>

This brings in fireflyframework-cache (the cache abstraction), the r2dbc-postgresql driver, r2dbc-pool and the R2DBC SPI transitively.

If you are not inheriting the Firefly parent, import the BOM in your dependencyManagement and let it manage the version:

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.fireflyframework</groupId>
<artifactId>fireflyframework-bom</artifactId>
<version>${firefly.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

Quick Start

  1. Add the dependencies — the cache core and this adapter:
<dependencies>
<dependency>
<groupId>org.fireflyframework</groupId>
<artifactId>fireflyframework-cache</artifactId>
</dependency>
<dependency>
<groupId>org.fireflyframework</groupId>
<artifactId>fireflyframework-cache-postgres</artifactId>
</dependency>
</dependencies>
  1. Enable the provider and point it at your database in application.yml:
firefly:
cache:
default-cache-type: POSTGRES # select this adapter (or AUTO to let the core pick)postgres:
enabled: truehost: localhostport: 5432database: appdbusername: apppassword: secret
  1. Use the unified cache API — obtain a CacheAdapter from the core FireflyCacheManager; it is now backed by PostgreSQL with no other code changes:
importorg.fireflyframework.cache.core.CacheAdapter;
importorg.fireflyframework.cache.manager.FireflyCacheManager;
importorg.springframework.stereotype.Service;
importreactor.core.publisher.Mono;
@ServicepublicclassProductService {
privatefinalCacheAdaptercache;
publicProductService(FireflyCacheManagercacheManager) {
this.cache = cacheManager.getCache("products"); // CacheType.POSTGRES when selected
}
publicMono<Product> findById(Stringid) {
returncache.<String, Product>get(id, Product.class)
.flatMap(opt -> opt.map(Mono::just).orElseGet(() ->
loadFromDatabase(id).flatMap(p -> cache.put(id, p).thenReturn(p))));
}
}

Because every adapter implements the same CacheAdapter SPI, switching between Caffeine, Redis, Hazelcast, JCache and PostgreSQL is a configuration change (firefly.cache.default-cache-type) plus the matching dependency — application code stays the same.

Configuration

All adapter-specific properties live under the firefly.cache.postgres prefix and are bound by the core CacheProperties.PostgresConfig. The active provider is chosen by the core-owned firefly.cache.default-cache-type key.

firefly:
cache:
default-cache-type: POSTGRES # core: choose the active provider (default: CAFFEINE)postgres:
enabled: true # default: falsehost: localhost # default: localhostport: 5432# default: 5432database: appdb # default: noneusername: app # default: nonepassword: secret # default: noneschema: public # default: publiccache-table: firefly_cache_entries # default: firefly_cache_entriescache-name: default # default: defaultkey-prefix: ""# default: emptydefault-ttl: PT30M # default: 30 minutesauto-create-schema: true # default: truemax-pool-size: 10# default: 10min-pool-size: 1# default: 1properties: {} # extra R2DBC driver options
PropertyTypeDefaultDescription
firefly.cache.default-cache-typeCacheTypeCAFFEINECore property selecting the active provider. Set to POSTGRES (or AUTO) to use this adapter.
firefly.cache.postgres.enabledbooleanfalseEnables the PostgreSQL cache provider and its auto-configuration.
firefly.cache.postgres.hostStringlocalhostPostgreSQL host.
firefly.cache.postgres.portint5432PostgreSQL port.
firefly.cache.postgres.databaseStringDatabase name.
firefly.cache.postgres.usernameStringDatabase username.
firefly.cache.postgres.passwordStringDatabase password.
firefly.cache.postgres.schemaStringpublicSchema that holds the cache table.
firefly.cache.postgres.cache-tableStringfirefly_cache_entriesCache table name.
firefly.cache.postgres.cache-nameStringdefaultDefault logical cache name.
firefly.cache.postgres.key-prefixString""Prefix applied to namespaced keys.
firefly.cache.postgres.default-ttlDurationPT30MDefault entry TTL; zero/negative means no expiration.
firefly.cache.postgres.auto-create-schemabooleantrueCreate the cache table and expiry index on startup.
firefly.cache.postgres.max-pool-sizeint10Maximum R2DBC pool connections.
firefly.cache.postgres.min-pool-sizeint1Initial/minimum R2DBC pool connections.
firefly.cache.postgres.propertiesMap{}Extra options passed to the PostgreSQL R2DBC driver.

Connection settings come only from firefly.cache.postgres.*, never from spring.r2dbc.* — the cache uses a dedicated, pooled ConnectionFactory bean (fireflyCachePostgresConnectionFactory) independent of your application datasource.

Storage schema

When auto-create-schema is true, the adapter provisions (idempotently) the following table and a partial index supporting expiry scans:

CREATETABLEIF NOT EXISTS "public"."firefly_cache_entries" (
cache_name VARCHAR(255) NOT NULL,
cache_key VARCHAR(1024) NOT NULL,
value BYTEANOT NULL,
expires_at TIMESTAMPTZ,
created_at TIMESTAMPTZNOT NULL DEFAULT NOW(),
PRIMARY KEY (cache_name, cache_key)
);
CREATEINDEXIF NOT EXISTS firefly_cache_entries_expires_idx
ON"public"."firefly_cache_entries" (expires_at) WHERE expires_at IS NOT NULL;

If you disable auto-create-schema, create this table and index manually before first use.

How It Works

  • PostgresCacheAutoConfiguration (org.fireflyframework.cache.config) — Spring Boot @AutoConfiguration registered in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. Activates when the R2DBC SPI is on the classpath and firefly.cache.postgres.enabled=true. Builds the pooled fireflyCachePostgresConnectionFactory bean and, when enabled, provisions the schema.
  • PostgresProvider (org.fireflyframework.cache.spi.providers) — the CacheProviderFactory SPI implementation, registered for ServiceLoader in META-INF/services/org.fireflyframework.cache.spi.CacheProviderFactory. Reports CacheType.POSTGRES with priority 15 and is available when the connection factory is present and the provider is enabled.
  • PostgresCacheHelper (org.fireflyframework.cache.factory) — the factory helper the cache core references reflectively to build adapters; its fully-qualified name is load-bearing and must not be renamed or moved.
  • PostgresCacheAdapter (org.fireflyframework.cache.adapter.postgres) — the CacheAdapter implementation. Stores entries in the relational table via the R2DBC ConnectionFactory, serializes values with JsonCacheSerializer, namespaces keys per cache, enforces TTL, and reports CacheStats/CacheHealth.
  • PostgresCacheConfig (org.fireflyframework.cache.adapter.postgres) — immutable per-adapter config (cache name, key prefix, default TTL, schema, table).

Documentation

Contributing

Contributions are welcome. Please read the CONTRIBUTING.md guide for details on our code of conduct, development process, and how to submit pull requests.

License

Copyright 2024-2026 Firefly Software Foundation.

Licensed under the Apache License, Version 2.0. See LICENSE for details.

About

PostgreSQL/R2DBC cache provider adapter for the Firefly Framework reactive cache abstraction for Spring Boot

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors