Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

README.md

⚡ Bus Cache: High-Performance Multi-Level Caching Framework

Unified Caching Interface Supporting Multiple Storage Backends


📖 Project Introduction

Bus Cache is an enterprise-level caching framework that provides a unified caching abstraction supporting multiple storage implementations. It offers seamless integration with various cache providers while maintaining a consistent API, enabling developers to switch between different caching technologies without code changes.

The framework features declarative caching through annotations, automatic cache key generation, cache penetration prevention, and comprehensive metrics monitoring.


✨ Core Features

🎯 Unified Cache Interface

  • Provider Agnostic: Single API for all cache implementations (Redis, Caffeine, Memcached, etc.)
  • Zero Migration Cost: Switch between cache providers by configuration only
  • Type-Safe Generics: Full support for generic key-value pairs with compile-time safety
  • Batch Operations: Optimized bulk read/write operations for improved performance

⚡ Multiple Cache Implementations

Implementation Use Case Performance
MemoryCache Local in-memory caching $\text{Latency } < 1\text{ms}$
CaffeineCache High-performance local cache Hit Rate $> 95%$
GuavaCache Guava-based local cache Hit Rate $> 90%$
RedisCache Distributed single-node Redis Network Latency
RedisClusterCache Distributed Redis Cluster Network Latency
MemcachedCache Memcached distributed cache Network Latency
NoOpCache Testing/No-op cache N/A

🚀 Declarative Caching

  • @Cached: Read-through caching with automatic write-on-miss
  • @CachedGet: Read-only cache access without automatic updates
  • @Invalid: Cache invalidation based on method execution
  • @CacheKey: Custom cache key generation using SpEL expressions

🛡️ Advanced Features

  • Cache Penetration Prevention: Automatic placeholder insertion for null results
  • SpEL Support: Dynamic cache key generation using Spring Expression Language
  • Conditional Caching: Cache based on runtime conditions with SpEL
  • Flexible Expiration: Per-entry or global TTL configuration, with CacheExpire.FOREVER (0) meaning never expire
  • TTL Refresh: renew(key, expire) extends the TTL of an existing entry without changing the key
  • Atomic Counters: increment(key) provides monotonic counters for rate limits, sequences, and lightweight metrics
  • Statistics Integration: Built-in cache hit rate statistics and monitoring
  • Multi-Key Caching: Batch operations with collection-based cache keys

🚀 Quick Start

Maven Dependency

<dependency>
    <groupId>org.miaixz</groupId>
    <artifactId>bus-cache</artifactId>
    <version>x.x.x</version>
</dependency>

Spring Boot Integration

1. Add Starter Dependency

<dependency>
    <groupId>org.miaixz</groupId>
    <artifactId>bus-starter</artifactId>
    <version>x.x.x</version>
</dependency>

2. Configure Cache

CacheProperties (bus.cache.*) supports the following fields:

Property Type Description
type String Fully-qualified class name of the Collector implementation
prefix String Global cache key prefix applied to all keys
timeout String Default expiration timeout (e.g. "3600000", mainly used by Redis)
provider.url String JDBC URL for database-backed Collector (MySQL, H2, SQLite, etc.)
provider.username String Database username
provider.password String Database password

Cache instances (MemoryCache, CaffeineCache, RedisCache, etc.) are interface types and must be registered as Spring Beans — they cannot be bound from YAML.

Example: in-memory collector (no persistence)

bus:
  cache:
    type: org.miaixz.bus.cache.collect.MemoryCollector
    prefix: myapp

Example: MySQL-backed collector (survives restarts)

bus:
  cache:
    type: org.miaixz.bus.cache.collect.MySQLCollector
    prefix: myapp
    provider:
      url: jdbc:mysql://localhost:3306/mydb
      username: root
      password: secret

Example: bus-metrics adapter (Prometheus / Micrometer / OTel)

bus:
  cache:
    type: org.miaixz.bus.metrics.builtin.CacheMetricsAdapter
    prefix: myapp

3. Enable Caching

@SpringBootApplication
@EnableCache
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public CacheX<String, Object> memoryCache() {
        return new MemoryCache<>(1000, 180000);
    }

    @Bean
    public CacheX<String, Object> caffeineCache() {
        return new CaffeineCache<>(10000, 3600000);
    }

    @Bean
    public CacheX<String, Object> redisCache(JedisPool jedisPool) {
        return new RedisCache<>(jedisPool, new Hessian2Serializer());
    }
}

📝 Usage Examples

1. Basic Cache Operations

Using MemoryCache

@Service
public class UserService {

    private final CacheX<String, User> cache;

    public UserService() {
        // Create a local memory cache
        this.cache = new MemoryCache<>(1000, 180000);
    }

    public User getUser(String userId) {
        // Try to read from cache
        User user = cache.read(userId);

        if (user == null) {
            // Cache miss - fetch from database
            user = userRepository.findById(userId);

            // Write to cache with 30-minute expiration
            cache.write(userId, user, 30 * 60 * 1000);
        }

        return user;
    }

    public void updateUser(String userId, User user) {
        // Update database
        userRepository.save(user);

        // Update cache
        cache.write(userId, user, 30 * 60 * 1000);
    }

    public void deleteUser(String userId) {
        // Delete from database
        userRepository.deleteById(userId);

        // Remove from cache
        cache.remove(userId);
    }
}

Using CaffeineCache

@Service
public class ProductService {

    private final CacheX<String, Product> cache;

    public ProductService() {
        // High-performance Caffeine cache
        this.cache = new CaffeineCache<>(
            10000,                    // Maximum 10,000 entries
            60 * 60 * 1000           // 1-hour expiration
        );
    }

    public Product getProduct(String productId) {
        return cache.read(productId);
    }

    public Map<String, Product> getProducts(List<String> productIds) {
        // Batch read - optimized for performance
        return cache.read(productIds);
    }

    public void cacheProducts(Map<String, Product> products) {
        // Batch write
        cache.write(products, 60 * 60 * 1000);
    }
}

2. Declarative Caching with Annotations

@Cached - Read-Through Caching

@Service
public class OrderService {

    @Cached(name = "redis", prefix = "order:", expire = 30 * 60 * 1000)
    public Order getOrderById(String orderId) {
        // This method's result is cached
        // Cache key: order:{orderId}
        return orderRepository.findById(orderId);
    }

    @Cached(
        name = "caffeine",
        prefix = "user:",
        expire = 60 * 60 * 1000,
        condition = "#userId != null"
    )
    public User getUserById(String userId) {
        // Only cached if userId is not null
        // Cache key: user:{userId}
        return userRepository.findById(userId);
    }
}

@CachedGet - Read-Only Cache

@Service
public class ConfigurationService {

    @CachedGet(name = "memory", prefix = "config:")
    public String getConfig(String key) {
        // Read from cache without automatic write on miss
        return configRepository.findByKey(key);
    }
}

@Invalid - Cache Invalidation

@Service
public class UserService {

    @Cached(name = "redis", prefix = "user:", expire = 60 * 60 * 1000)
    public User getUser(String userId) {
        return userRepository.findById(userId);
    }

    @Invalid(name = "redis", prefix = "user:")
    public void updateUser(String userId, User user) {
        // Removes cached user when updating
        userRepository.save(user);
    }

    @Invalid(name = "redis", prefix = "user:")
    public void deleteUser(String userId) {
        // Removes cached user when deleting
        userRepository.deleteById(userId);
    }
}

3. Advanced Cache Key Generation

Using @CacheKey with SpEL

@Service
public class ProductService {

    @Cached(name = "redis", prefix = "product:")
    public Product getProduct(
        @CacheKey String productId,
        @CacheKey("#region.toUpperCase()") String region
    ) {
        // Cache key: product:{productId}{region}
        // Example: product:12345US
        return productRepository.findByIdAndRegion(productId, region);
    }

    @Cached(name = "caffeine", prefix = "user:")
    public User getUser(
        @CacheKey("#userId") String userId,
        @CacheKey("#type.name()") UserType type
    ) {
        // Cache key: user:{userId}{type}
        // Example: user:12345PREMIUM
        return userRepository.findByIdAndType(userId, type);
    }
}

Multi-Key Batch Operations

@Service
public class UserService {

    @Cached(name = "redis", prefix = "user:")
    public Map<String, User> getUsers(@CacheKey Collection<String> userIds) {
        // Generates separate cache key for each user ID
        // Example: user:12345, user:67890
        return userRepository.findByIds(userIds);
    }

    @Cached(name = "caffeine", prefix = "product:")
    public List<Product> getProductsByIds(@CacheKey List<String> productIds) {
        // Batch cache lookup with automatic key generation
        return repository.findAllById(productIds);
    }
}

4. Distributed Redis Cache

Single-Node Redis

@Configuration
public class RedisCacheConfig {

    @Bean
    public JedisPool jedisPool() {
        return new JedisPool("localhost", 6379);
    }

    @Bean
    public CacheX<String, Object> redisCache(JedisPool jedisPool) {
        // Use Hessian2 serializer for better performance
        return new RedisCache<>(jedisPool, new Hessian2Serializer());
    }

    @Bean
    public CacheX<String, Object> redisJsonCache(JedisPool jedisPool) {
        // Use JSON serializer for better compatibility
        return new RedisCache<>(jedisPool, new FastJsonSerializer());
    }
}

RedisCache implements AutoCloseable. Call close() (or use try-with-resources) to return all pool connections. The @PreDestroy annotation ensures automatic shutdown in Spring containers.

Redis Cluster

@Configuration
public class RedisClusterConfig {

    @Bean
    public JedisCluster jedisCluster() {
        Set<HostAndPort> nodes = new HashSet<>();
        nodes.add(new HostAndPort("localhost", 7000));
        nodes.add(new HostAndPort("localhost", 7001));
        nodes.add(new HostAndPort("localhost", 7002));

        return new JedisCluster(nodes);
    }

    @Bean
    public CacheX<String, Object> redisClusterCache(JedisCluster jedisCluster) {
        return new RedisClusterCache<>(jedisCluster, new Hessian2Serializer());
    }
}

RedisClusterCache also implements AutoCloseable.

Cluster scan limitation: Redis Cluster does not support cross-slot SCAN through a single cursor. RedisClusterCache.scan(prefix) performs a best-effort scan reachable from cursor "0". For complete coverage across all shards, route all keys to the same slot using a hash tag (e.g. {ns}:key).

5. Advanced CacheX Operations

Permanent (Never-Expire) Entries

Pass CacheExpire.FOREVER (0) as the expiry argument to persist an entry indefinitely:

// Store configuration that must never expire
cache.write("global:config", config, CacheExpire.FOREVER);

All implementations honour this contract:

  • MemoryCache — entry is never evicted by TTL checks
  • RedisCache / RedisClusterCache — issues a plain SET without PX
  • MemcachedCache — falls back to Memcached's maximum TTL of 30 days (protocol limitation)

TTL Refresh (renew)

Extend the lifetime of an existing entry without rewriting its value:

// Extend session TTL on each user interaction
boolean refreshed = cache.renew(sessionKey, CacheExpire.HALF_HOUR);
if (!refreshed) {
    // Key has already expired — recreate the session
}

RedisCache and RedisClusterCache implement renew via a single PEXPIRE command. MemoryCache and other implementations use a read-then-write fallback.

Atomic Counters (increment)

Atomically increment a counter and retrieve the new value:

// Rate limiting: track requests per client per minute
long count = cache.increment("ratelimit:" + clientId + ":" + minute);
if (count > MAX_REQUESTS_PER_MINUTE) {
    throw new RateLimitException();
}
  • Redis: backed by the native INCR command — atomicity guaranteed even under concurrency.
  • MemoryCache: backed by AtomicLong — also thread-safe.
  • Counters start at 1 on first call (key created with value 0, then incremented).
  • Counter keys share no TTL by default; call remove() explicitly to reset.

Prefix Scan (scan)

Retrieve all entries whose key begins with a given prefix:

// Fetch all cached items under a namespace
Map<String, Object> items = cache.scan("product:category:electronics:");

Note: For RedisClusterCache, cross-shard scan is not fully supported. See the cluster scan limitation note above.

6. Cache Statistics and Monitoring

Accessing Cache Statistics

@Service
public class CacheMonitorService {

    @Autowired
    private Collector cacheCollector;

    public void printCacheStats() {
        Map<String, Snapshot> stats = cacheCollector.getHitting();

        stats.forEach((pattern, snapshot) -> {
            System.out.println("Cache Pattern: " + pattern);
            System.out.println("  Hits: " + snapshot.getHit());
            System.out.println("  Requests: " + snapshot.getRequired());
            System.out.println("  Hit Rate: " + snapshot.getRate());
        });
    }

    public void resetCacheStats(String pattern) {
        cacheCollector.reset(pattern);
    }
}

MemoryCache Statistics

@Service
public class LocalCacheService {

    private final MemoryCache<String, User> cache = new MemoryCache<>(1000, 180000);

    public void printStats() {
        // Get built-in statistics
        String stats = cache.getStats();
        System.out.println(stats);

        // Output example:
        // MemoryCacheStats[requests=10000, hits=9500, hitRate=95.00%, size=800]

        // Get estimated size
        long size = cache.estimatedSize();
        System.out.println("Current cache size: " + size);
    }
}

CaffeineCache Statistics

@Service
public class CaffeineCacheService {

    private final CaffeineCache<String, Product> cache =
        new CaffeineCache<>(10000, 3600000);

    public void printStats() {
        // Get Caffeine's native statistics
        String stats = cache.getStats();
        System.out.println(stats);

        // Access native cache for advanced operations
        com.github.benmanes.caffeine.cache.Cache<String, Product> nativeCache =
            cache.getNativeCache();

        CacheStats caffeineStats = nativeCache.stats();
        System.out.println("Hit Rate: " + caffeineStats.hitRate());
        System.out.println("Eviction Count: " + caffeineStats.evictionCount());
    }
}

7. Cache Penetration Prevention

@Service
public class SecureUserService {

    @Cached(
        name = "redis",
        prefix = "user:",
        expire = 60 * 60 * 1000,
        enablePenetrationProtect = true
    )
    public User getUser(String userId) {
        User user = userRepository.findById(userId);

        if (user == null) {
            // Cache penetration prevention:
            // Automatically caches a placeholder object
            // to prevent repeated database queries for non-existent users
            return null;
        }

        return user;
    }
}

8. Conditional Caching

@Service
public class ConditionalCacheService {

    @Cached(
        name = "caffeine",
        prefix = "premium_user:",
        condition = "#user.type == T(com.example.UserType).PREMIUM"
    )
    public UserProfile getPremiumProfile(User user) {
        // Only cache premium users
        return profileRepository.findByUserId(user.getId());
    }

    @Cached(
        name = "redis",
        prefix = "product:",
        condition = "#price > 0 && #price < 10000"
    )
    public Product getProductInRange(String productId, BigDecimal price) {
        // Only cache products within price range
        return productRepository.findById(productId);
    }

    @Cached(
        name = "memory",
        prefix = "config:",
        condition = "#env == 'production'"
    )
    public String getProductionConfig(String key, String env) {
        // Only cache in production environment
        return configRepository.get(key);
    }
}

9. Custom Serializer

@Configuration
public class SerializerConfig {

    @Bean
    public CacheX<String, Object> customRedisCache(JedisPool jedisPool) {
        // Custom serializer for specific serialization needs
        BaseSerializer serializer = new BaseSerializer() {
            @Override
            public byte[] serialize(Object obj) {
                // Custom serialization logic
                return CustomSerializer.encode(obj);
            }

            @Override
            public Object deserialize(byte[] bytes) {
                // Custom deserialization logic
                return CustomSerializer.decode(bytes);
            }
        };

        return new RedisCache<>(jedisPool, serializer);
    }
}

💡 Best Practices

1. Choose the Right Cache Implementation

// ✅ Recommended: Use MemoryCache for short-lived, frequently accessed data
CacheX<String, Session> sessionCache = new MemoryCache<>(1000, 180000);

// ✅ Recommended: Use CaffeineCache for high-performance local caching
CacheX<String, Product> productCache = new CaffeineCache<>(10000, 3600000);

// ✅ Recommended: Use RedisCache for distributed caching
CacheX<String, User> userCache = new RedisCache<>(jedisPool, new Hessian2Serializer());

// ❌ Not Recommended: Use distributed cache for local session data
// Unnecessary network overhead

2. Set Appropriate Expiration Times

// ✅ Recommended: Short expiration for frequently changing data
@Cached(name = "redis", prefix = "stock:", expire = 60 * 1000)  // 1 minute
public Stock getStock(String symbol) {
    return stockRepository.getRealtime(symbol);
}

// ✅ Recommended: Long expiration for rarely changing data
@Cached(name = "caffeine", prefix = "config:", expire = 24 * 60 * 60 * 1000)  // 24 hours
public Config getConfig(String key) {
    return configRepository.findByKey(key);
}

// ❌ Not Recommended: No expiration for dynamic data
@Cached(name = "redis", prefix = "price:", expire = CacheExpire.FOREVER)
public Price getPrice(String productId) {
    // FOREVER = 0: entry persists until explicitly removed — stale data served indefinitely
}

3. Use Batch Operations

// ✅ Recommended: Batch read for better performance
public Map<String, User> getUsers(List<String> userIds) {
    return cache.read(userIds);  // Single operation
}

// ❌ Not Recommended: Loop read
public Map<String, User> getUsers(List<String> userIds) {
    Map<String, User> result = new HashMap<>();
    for (String userId : userIds) {
        result.put(userId, cache.read(userId));  // Multiple operations
    }
    return result;
}

4. Implement Cache Aside Pattern Properly

// ✅ Recommended: Proper cache-aside implementation
public Product getProduct(String productId) {
    Product product = cache.read(productId);

    if (product == null) {
        product = productRepository.findById(productId);

        if (product != null) {
            cache.write(productId, product, 3600000);
        }
    }

    return product;
}

// ❌ Not Recommended: No null check causes repeated DB queries
public Product getProduct(String productId) {
    Product product = cache.read(productId);

    if (product == null) {
        // Caches null results - repeated for non-existent products
        product = productRepository.findById(productId);
        cache.write(productId, product, 3600000);
    }

    return product;
}

5. Monitor Cache Performance

// ✅ Recommended: Regular monitoring and alerting
@Scheduled(fixedRate = 60000)  // Every minute
public void monitorCache() {
    Map<String, Snapshot> stats = cacheCollector.getHitting();

    stats.forEach((pattern, snapshot) -> {
        double hitRate = Double.parseDouble(snapshot.getRate().replace("%", ""));

        if (hitRate < 80.0) {
            logger.warn("Low cache hit rate for {}: {}", pattern, snapshot.getRate());
            // Send alert or adjust cache configuration
        }
    });
}

6. Use Proper Cache Key Design

// ✅ Recommended: Hierarchical cache keys
@Cached(prefix = "user:profile:")
public UserProfile getUserProfile(String userId) {
    // Cache key: user:profile:{userId}
}

@Cached(prefix = "user:settings:")
public UserSettings getUserSettings(String userId) {
    // Cache key: user:settings:{userId}
}

// ❌ Not Recommended: Flat namespace
@Cached(prefix = "cache_")
public Object getData(String type, String id) {
    // Easy conflicts, hard to invalidate
}

7. Always Close Cache Resources

All cache implementations that hold external connections (RedisCache, RedisClusterCache, MemcachedCache) implement AutoCloseable. Release resources when they are no longer needed:

// ✅ Spring beans: @PreDestroy is triggered automatically — no manual close() required
@Bean
public CacheX<String, Object> redisCache(JedisPool jedisPool) {
    return new RedisCache<>(jedisPool);  // closed automatically on context shutdown
}

// ✅ Non-Spring usage: use try-with-resources
try (RedisCache<String, Object> cache = new RedisCache<>(jedisPool)) {
    cache.write("key", value, CacheExpire.ONE_HOUR);
}

// ✅ Prefer renew() over read+write when only the TTL needs extending
boolean extended = cache.renew(sessionKey, CacheExpire.HALF_HOUR);

❓ Frequently Asked Questions

Q1: How do I switch between different cache implementations?

A: Simply change the configuration or bean definition:

// Before: MemoryCache
@Bean
public CacheX<String, Object> cache() {
    return new MemoryCache<>(1000, 180000);
}

// After: RedisCache (no code changes needed in service layer)
@Bean
public CacheX<String, Object> cache(JedisPool jedisPool) {
    return new RedisCache<>(jedisPool);
}

Q2: How do I handle cache serialization errors?

A: Use appropriate serializers:

// For Java Serializable objects
CacheX<String, Object> cache = new RedisCache<>(jedisPool, new JdkSerializer());

// For better performance (Hessian)
CacheX<String, Object> cache = new RedisCache<>(jedisPool, new Hessian2Serializer());

// For JSON compatibility
CacheX<String, Object> cache = new RedisCache<>(jedisPool, new FastJsonSerializer());

// For compression (reduces memory usage)
CacheX<String, Object> cache = new RedisCache<>(jedisPool, new JdkGzipSerializer());

Q3: How do I prevent cache stampede (thundering herd)?

A: Use cache lock mechanisms or refresh-ahead:

@Service
public class SafeUserService {

    private final Lock lock = new ReentrantLock();

    public User getUser(String userId) {
        User user = cache.read(userId);

        if (user == null) {
            lock.lock();
            try {
                // Double-check locking
                user = cache.read(userId);
                if (user == null) {
                    user = userRepository.findById(userId);
                    cache.write(userId, user, 3600000);
                }
            } finally {
                lock.unlock();
            }
        }

        return user;
    }
}

Q4: How do I implement multi-level caching?

A: Use L1 (local) + L2 (distributed) cache pattern:

@Service
public class MultiLevelCacheService {

    private final CacheX<String, User> l1Cache = new CaffeineCache<>(1000, 60000);
    private final CacheX<String, User> l2Cache = new RedisCache<>(jedisPool);

    public User getUser(String userId) {
        // Try L1 cache first
        User user = l1Cache.read(userId);

        if (user == null) {
            // Try L2 cache
            user = l2Cache.read(userId);

            if (user == null) {
                // Fetch from database
                user = userRepository.findById(userId);

                // Populate both caches
                l2Cache.write(userId, user, 3600000);
                l1Cache.write(userId, user, 60000);
            } else {
                // Populate L1 from L2
                l1Cache.write(userId, user, 60000);
            }
        }

        return user;
    }
}

Q5: How do I invalidate related cache entries?

A: Use @Invalid annotation with proper key patterns:

@Service
public class CacheInvalidationService {

    @Cached(prefix = "user:")
    public User getUser(String userId) {
        return userRepository.findById(userId);
    }

    @Cached(prefix = "user:orders:")
    public List<Order> getUserOrders(String userId) {
        return orderRepository.findByUserId(userId);
    }

    @Invalid(prefix = "user:")
    @Invalid(prefix = "user:orders:")
    public void updateUser(String userId, User user) {
        // Invalidates both user and user orders caches
        userRepository.save(user);
    }
}

Q6: How do I debug cache key generation?

A: Enable debug logging and inspect generated keys:

# application.yml
logging:
  level:
    org.miaixz.bus.cache: DEBUG
// Programmatic key generation
@Component
public class CacheKeyDebugger {

    public void debugKeyGeneration(Method method, Object[] args) {
        AnnoHolder holder = CacheInfoContainer.getCacheInfo(method).getLeft();

        if (holder.isMulti()) {
            Map[] maps = Builder.generateMultiKey(holder, args);
            System.out.println("Generated keys: " + maps[1].keySet());
        } else {
            String key = Builder.generateSingleKey(holder, args);
            System.out.println("Generated key: " + key);
        }
    }
}

Q7: How do I handle cache warmup?

A: Implement cache preloading on startup:

@Component
public class CacheWarmupService implements ApplicationRunner {

    @Autowired
    private CacheX<String, Product> productCache;

    @Autowired
    private ProductRepository productRepository;

    @Override
    public void run(ApplicationArguments args) {
        // Preload hot data into cache
        List<Product> hotProducts = productRepository.findHotProducts();

        Map<String, Product> productMap = hotProducts.stream()
            .collect(Collectors.toMap(
                Product::getId,
                Function.identity()
            ));

        productCache.write(productMap, 3600000);

        logger.info("Cache warmup completed: {} products loaded", hotProducts.size());
    }
}

🔄 Version Compatibility

Bus Cache Version JDK Version Spring Boot Version
8.x 17+ 3.x+
7.x 11+ 2.x+

📊 Performance Comparison

Based on benchmark tests (10,000 operations):

Cache Implementation Avg Latency Throughput Hit Rate
MemoryCache $0.5\text{ms}$ $20,000\text{ ops/s}$ $95%$
CaffeineCache $0.3\text{ms}$ $33,000\text{ ops/s}$ $98%$
GuavaCache $0.6\text{ms}$ $16,000\text{ ops/s}$ $92%$
RedisCache $2.5\text{ms}$ $4,000\text{ ops/s}$ N/A
MemcachedCache $2.0\text{ms}$ $5,000\text{ ops/s}$ N/A

🛠️ Configuration Reference

MemoryCache Properties

Property Type Default Description
maximumSize long 1000 Maximum number of entries
expireAfterWrite long 180000 TTL in milliseconds
expireAfterAccess long 0 TTI in milliseconds (0 = disabled)
initialCapacity int 16 Initial map capacity

CaffeineCache Properties

Property Type Default Description
maximumSize long 1000 Maximum number of entries
expireAfterWrite long - TTL in milliseconds
expireAfterAccess long - TTI in milliseconds
initialCapacity int - Initial map capacity

RedisCache Properties

Property Type Default Description
host String localhost Redis server host
port int 6379 Redis server port
timeout int 2000 Connection timeout (ms)
serializer BaseSerializer Hessian2Serializer Value serializer

🔗 Related Documentation


📄 License

Apache License Version 2.0


Would you like to search for a tutorial video on YouTube for getting started with Bus Cache?