{"id":29652486,"url":"https://github.com/hunghhdev/pgcache","last_synced_at":"2026-05-16T11:04:49.574Z","repository":{"id":303849269,"uuid":"1016908409","full_name":"hunghhdev/pgcache","owner":"hunghhdev","description":"Simple and reliable PostgreSQL-backed caching solution for Spring-based applications","archived":false,"fork":false,"pushed_at":"2026-05-12T01:11:01.000Z","size":320,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-12T03:13:01.503Z","etag":null,"topics":["cache","java","postgresql","spring"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hunghhdev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-09T18:00:58.000Z","updated_at":"2026-05-12T01:07:43.000Z","dependencies_parsed_at":"2025-07-10T03:33:13.653Z","dependency_job_id":"0acca426-dbc4-42d2-a78f-cfff95085a60","html_url":"https://github.com/hunghhdev/pgcache","commit_stats":null,"previous_names":["hunghhdev/pgcache"],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/hunghhdev/pgcache","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hunghhdev%2Fpgcache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hunghhdev%2Fpgcache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hunghhdev%2Fpgcache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hunghhdev%2Fpgcache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hunghhdev","download_url":"https://codeload.github.com/hunghhdev/pgcache/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hunghhdev%2Fpgcache/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33100321,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-16T04:41:52.686Z","status":"ssl_error","status_checked_at":"2026-05-16T04:41:52.009Z","response_time":115,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cache","java","postgresql","spring"],"created_at":"2025-07-22T06:01:37.088Z","updated_at":"2026-05-16T11:04:49.566Z","avatar_url":"https://github.com/hunghhdev.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PgCache\n\n[![Java](https://img.shields.io/badge/Java-11%2B-blue.svg)](https://www.oracle.com/java/)\n[![Maven Central](https://img.shields.io/badge/Maven%20Central-1.7.0-green.svg)](https://central.sonatype.com/artifact/io.github.hunghhdev/pgcache)\n[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)\n\n**A simple caching library that uses your existing PostgreSQL as cache backend.**\n\nPerfect for small-to-medium applications that want caching without the complexity and cost of dedicated cache infrastructure.\n\n## Why PgCache?\n\n**You already have PostgreSQL. Why add Redis?**\n\n| Scenario | Traditional Approach | With PgCache |\n|----------|---------------------|--------------|\n| Small/Medium app | PostgreSQL + Redis | PostgreSQL only |\n| Infrastructure | 2 systems to maintain | 1 system |\n| Monthly cost | -200+ for Redis | /usr/bin/zsh extra |\n| Complexity | Connection pools, failover for both | Single database |\n\n### Best For\n\n- Startups and small teams wanting to keep infrastructure simple\n- Applications with moderate caching needs (\u003c 100k cache entries)\n- Projects where PostgreSQL is already the primary database\n- When you need caching but Redis/Memcached is overkill\n\n### Not Ideal For\n\n- High-throughput systems needing millions of ops/sec\n- Sub-millisecond latency requirements\n- Systems already using Redis with complex data structures\n- Very large cache datasets (\u003e 1M entries)\n\n## Features\n\n- **Zero extra infrastructure** - Uses your existing PostgreSQL\n- **Spring Boot integration** - Works with `@Cacheable`, `@CacheEvict`, auto-configuration\n- **Quarkus integration** - Works with `@CacheResult`, MicroProfile Health\n- **Async API** - Non-blocking operations (`getAsync`, `putAsync`)\n- **Event Listeners** - Monitor cache events (put, evict, clear)\n- **Sliding TTL** - Active entries stay cached longer (like Redis)\n- **Null value caching** - Properly cache null results\n- **Batch operations** - `getAll`, `putAll`, `evictAll` for efficiency\n- **Cache statistics** - Hit/miss counts, hit rate monitoring\n- **Micrometer metrics** - Auto-configured metrics for monitoring\n- **Pattern eviction** - Evict keys by pattern (e.g., `user:%`)\n- **JSONB storage** - Efficient storage with PostgreSQL's native JSON\n- **UNLOGGED tables** - Optimized for cache performance\n- **Background cleanup** - Automatic expired entry removal\n\n## Quick Start\n\n### Maven\n\n```xml\n\u003c!-- Core library (standalone usage) --\u003e\n\u003cdependency\u003e\n  \u003cgroupId\u003eio.github.hunghhdev\u003c/groupId\u003e\n  \u003cartifactId\u003epgcache-core\u003c/artifactId\u003e\n  \u003cversion\u003e1.7.0\u003c/version\u003e\n\u003c/dependency\u003e\n\n\u003c!-- Spring Boot integration --\u003e\n\u003cdependency\u003e\n  \u003cgroupId\u003eio.github.hunghhdev\u003c/groupId\u003e\n  \u003cartifactId\u003epgcache-spring\u003c/artifactId\u003e\n  \u003cversion\u003e1.7.0\u003c/version\u003e\n\u003c/dependency\u003e\n\n\u003c!-- Quarkus integration --\u003e\n\u003cdependency\u003e\n  \u003cgroupId\u003eio.github.hunghhdev\u003c/groupId\u003e\n  \u003cartifactId\u003epgcache-quarkus\u003c/artifactId\u003e\n  \u003cversion\u003e1.7.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### Spring Boot Usage\n\n```java\n@SpringBootApplication\n@EnablePgCache\npublic class Application {\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n}\n\n@Service\npublic class UserService {\n\n    @Cacheable(\"users\")\n    public User getUser(Long id) {\n        return userRepository.findById(id);\n    }\n\n    @CacheEvict(\"users\")\n    public void deleteUser(Long id) {\n        userRepository.deleteById(id);\n    }\n}\n```\n\n### Configuration\n\n```yaml\n# application.yml\npgcache:\n  default-ttl: PT1H                    # 1 hour default TTL\n  allow-null-values: true\n  background-cleanup:\n    enabled: true\n    interval: PT30M                    # Cleanup every 30 minutes\n\n  caches:\n    users:\n      ttl: PT2H\n      ttl-policy: SLIDING              # Reset TTL on access\n    products:\n      ttl: PT6H\n      ttl-policy: ABSOLUTE             # Fixed expiration\n```\n\n### Quarkus Usage\n\n```java\n@ApplicationScoped\npublic class UserService {\n\n    @Inject\n    PgQuarkusCacheManager cacheManager;\n\n    public Uni\u003cUser\u003e getUser(Long id) {\n        PgQuarkusCache cache = (PgQuarkusCache) cacheManager.getCache(\"users\").get();\n        // Uses async API under the hood\n        return cache.getAsync(\"user:\" + id, key -\u003e userRepository.findById(id));\n    }\n}\n```\n\n**Quarkus Configuration:**\n\n```properties\n# application.properties\npgcache.default-ttl=PT1H\npgcache.allow-null-values=true\npgcache.ttl-policy=ABSOLUTE\npgcache.background-cleanup.enabled=true\npgcache.background-cleanup.interval=PT30M\n\n# Per-cache settings\npgcache.caches.users.ttl=PT2H\npgcache.caches.users.ttl-policy=SLIDING\n```\n\n### Standalone Usage (Core)\n\n```java\nPgCacheClient cache = PgCacheStore.builder()\n    .dataSource(dataSource)\n    .build();\n\n// Store with TTL\ncache.put(\"user:123\", user, Duration.ofHours(1));\n\n// Retrieve\nOptional\u003cUser\u003e user = cache.get(\"user:123\", User.class);\n\n// Async Operations (v1.6.2)\ncache.getAsync(\"user:123\", User.class)\n     .thenAccept(opt -\u003e System.out.println(opt.orElse(null)));\n\n// Key Check (v1.6.2) - Efficient existence check\nboolean exists = cache.containsKey(\"user:123\");\n\n// Pattern Operations\ncache.getKeys(\"user:%\"); // Get all user keys\ncache.evictByPattern(\"user:%\"); // Evict all user keys\n```\n\n### Event Listeners (v1.6.2)\n\nMonitor cache operations by registering listeners:\n\n```java\nPgCacheStore.builder()\n    .dataSource(dataSource)\n    .addEventListener(new CacheEventListener() {\n        @Override\n        public void onPut(String key, Object value) {\n            log.info(\"Cached: {}\", key);\n        }\n        @Override\n        public void onEvict(String key) {\n            log.info(\"Evicted: {}\", key);\n        }\n    })\n    .build();\n```\n\nIn **Spring Boot**, simply define a bean implementing `CacheEventListener`:\n\n```java\n@Component\npublic class MyCacheListener implements CacheEventListener {\n    // ... overrides\n}\n```\n\n### Quarkus Health Check (v1.6.2)\n\nAdd cache health status to your Quarkus Health endpoint:\n\n```java\n@Liveness\n@ApplicationScoped\npublic class CacheHealthCheck implements HealthCheck {\n    @Inject PgQuarkusHealthCheck pgCacheHealth;\n\n    @Override\n    public HealthCheckResponse call() {\n        return pgCacheHealth.check().isUp() \n             ? HealthCheckResponse.up(\"pgcache\") \n             : HealthCheckResponse.down(\"pgcache\");\n    }\n}\n```\n\n## Advanced Features\n\n### Batch Operations (v1.3.0)\n\n```java\n// Get multiple values at once\nMap\u003cString, User\u003e users = cache.getAll(\n    Arrays.asList(\"user:1\", \"user:2\", \"user:3\"),\n    User.class\n);\n\n// Put multiple values\nMap\u003cString, User\u003e entries = Map.of(\n    \"user:1\", user1,\n    \"user:2\", user2\n);\ncache.putAll(entries, Duration.ofHours(1));\n```\n\n### Cache Statistics\n\n```java\nCacheStatistics stats = cache.getStatistics();\nSystem.out.println(\"Hit Rate: \" + stats.getHitRate());\n```\n\n### Micrometer Metrics\n\nWhen using `pgcache-spring` with Micrometer, metrics are auto-configured:\n- `pgcache.gets` (counter)\n- `pgcache.puts` (counter)\n- `pgcache.evictions` (counter)\n- `pgcache.size` (gauge)\n- `pgcache.hit.rate` (gauge)\n\n## Sliding vs Absolute TTL\n\n```java\n// Absolute TTL: expires at creation_time + TTL\ncache.put(\"key\", value, Duration.ofHours(1));\n\n// Sliding TTL: expires at last_access_time + TTL\ncache.put(\"key\", value, Duration.ofHours(1), TTLPolicy.SLIDING);\n```\n\n## Performance\n\nPgCache uses **UNLOGGED tables** (no WAL overhead) and **JSONB** storage for performance.\n- **Read**: ~1-5ms\n- **Write**: ~2-10ms\n- **Throughput**: Hundreds to low thousands ops/sec\n\n## Migration\n\n### From 1.6.x to 1.7.0\n\nNo database changes required. Backward-compatible release with bug fixes and DRY refactor.\n\n**Bug fixes:**\n- Cache names containing `_` or `%` no longer cause cross-cache eviction (SQL `LIKE` wildcards now escaped).\n- Concurrent `setCacheConfiguration` / `removeCache` / `getCache` no longer racy.\n- `PgCacheManager.removeCache` returns `true` even when post-removal `clear()` fails.\n\n**New API:**\n- `PgCacheClient.size(String pattern)` — efficient `COUNT(*)` size lookup.\n- `TTLPolicy.parse(String)` / `parseOrDefault(String)`.\n- `NullValueMarker.MARKER_VALUE`, `NullValueMarker.isMarker(Object)`.\n- `SqlPatterns.escapeLikePattern(String)`.\n- `PgCacheManager.getStoreStatistics()` — per-store metrics for health endpoints.\n\n**Deprecations (removed in 2.0.0):**\n- `PgCacheStore(DataSource[, boolean])` ctors → use `PgCacheStore.builder()`.\n- `PgCacheManager.PgCacheConfiguration` → use `CacheStoreConfig`.\n- `PgCache.cleanupExpired()` / `PgQuarkusCache.cleanupExpired()` → use `cleanupExpiredAllCaches()`.\n- `PgQuarkusCacheManager.getOrCreateCache(String)` → use `(PgQuarkusCache) getCache(name).get()`.\n- `PgQuarkusCacheManager.CacheConfig`: `isAllowNullValues()` and 3-arg ctor removed; setter changed to `setAllowNullValues(Boolean)` for 3-state semantics.\n\n### From 1.5.x to 1.6.0\n\nNo database changes required.\n\n**New Features:**\n- **Async API**: `getAsync`, `putAsync` in `PgCacheClient`\n- **Key Operations**: `containsKey`, `getKeys(pattern)`\n- **Event Listeners**: `CacheEventListener` interface\n- **Quarkus**: `PgQuarkusCache` now uses non-blocking async internal API\n- **Spring**: Auto-detection of `CacheEventListener` beans\n\n### From 1.4.x to 1.5.x\n\nNo database changes required. Added Quarkus integration.\n\n### From 1.3.x to 1.4.0\n\nNo database changes required. Added Micrometer metrics.\n\n### From 1.2.x to 1.3.0\n\nNo database changes required. Added Batch operations and Statistics.\n\n### From 1.0.x/1.1.x to 1.2.x\n\nSchema update required for Sliding TTL:\n\n```sql\nALTER TABLE pgcache_store\n  ADD COLUMN IF NOT EXISTS ttl_policy VARCHAR(10) DEFAULT 'ABSOLUTE',\n  ADD COLUMN IF NOT EXISTS last_accessed TIMESTAMP DEFAULT now();\n\nCREATE INDEX IF NOT EXISTS pgcache_store_sliding_ttl_idx\n  ON pgcache_store (ttl_policy, last_accessed)\n  WHERE ttl_policy = 'SLIDING';\n```\n\n## License\n\nMIT License - see [LICENSE](LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhunghhdev%2Fpgcache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhunghhdev%2Fpgcache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhunghhdev%2Fpgcache/lists"}