Understanding the Region Concept in J2Cache Two-Tier Caching
The Region concept in J2Cache is derived from the region definition used in Ehcache. Most common caching utilities like Redis, Caffeine, and Guava Cache do not expose this concept natively, especially Redis which operates as a global hash table with no built-in region segmentation.
In real-world caching workflows, different datasets require distinct time-to-live (TTL) policies: some cached entries never expire, others are configured to invalidate after 30 minutes, 60 minutes, or other custom intervals. While Redis supports per-key TTL configuration, most in-memory Java caching frameworks (including Ehcache, Caffeine, Guava Cache) do not support per-key TTL settings, as this would introduce excessive management overhead and drastically degrade performance during stale entry checks. To solve this, in-memory cache frameworks group entries that share the same TTL policy for unified management, and these groups are what we refer to as regions.
J2Cache maps the Region concept to underlying caching implementations as follows:
| Underlying Cache | Corresponding Region Equivalent |
|---|---|
| Ehcache | region |
| Caffeine | Cache instance |
| Guava Cache | Cache instance |
When using Caffeine or Guava Cache, you first create a Cache instance with preconfigured eviction rules before storing any data, as shown in the sample code below:
// Initialize Caffeine builder with eviction policy parameters
Caffeine<Object, Object> cacheBuilder = Caffeine.newBuilder();
cacheBuilder = cacheBuilder.maximumSize(entryCapacity)
.expireAfterWrite(ttlDuration, TimeUnit.SECONDS);
// Build cache instance with fixed policies applied to all stored entries
Cache<String, Object> userInfoCache = cacheBuilder.build();
All entries written to this cache instance will follow the preconfigured TTL and capacity rules, you cannot override the TTL for individual keys after the cache is built. In contrast, Redis allows arbitrary per-key TTL configuration with no such restrictions.
J2Cache acts as an abstraction layer between in-memory L1 caches and centralized L2 caches like Redis, so it implements the Region concept to align with the limitations and capabilities of both cache tiers.
J2Cache uses Caffeine as its default L1 (in-memory) cache, with configuration stored in the caffeine.properties file. A sample configuration is shown below:
#########################################
# Caffeine L1 Cache Configuration
# Format: [region_name] = max_entry_count, ttl_duration[s|m|h|d]
#########################################
default = 1000, 30m
user_profile = 2000, 10m
blog_post = 5000, 1h
This configuration defines three separate cache regions:
- The default region, which holds a maximum of 1000 entries with a 30-minute TTL
- The
user_profileregion, capped at 2000 entries with a 10-minute TTL - The
blog_postregion, which can store up to 5000 entries with a 1-hour TTL
You can store user account data in the user_profile region and published blog content in the blog_post region, to apply separate eviction policies for each dataset type.
When you call the cache write method with the signature:
public void putCacheEntry(String regionName, String cacheKey, Object data)
If the regionName parameter (for example, order_data) is not preconfigured in caffeine.properties, J2Cache will automatically create a new region with that name, using the capacity and TTL values defined in the default region configuration.
To optimize cache performance and avoid unexpected eviction behavior, follow these guidelines:
- Plan dedicated regions for different business datasets based on functional requirements
- Adjust the maximum entry count and TTL for each region based on actual access patterns and data freshness needs
- Avoid using unconfigured custom regions, as these will inherit default settings that may not match your workload requirements