# CoSky Redis Configuration Best Practices for Performance and Reliability

> Boost Redis performance and reliability with CoSky configuration best practices. Learn to use Lua scripts, tune TTL, and enforce key naming for sub-millisecond latency.

- Repository: [Ahoo Wang/cosky](https://github.com/ahoo-wang/cosky)
- Tags: best-practices
- Published: 2026-02-23

---

**Use Lua scripts via `RedisConfigService` for atomic writes, deploy `RedisConsistencyConfigService` with tuned TTL for reads, and enforce consistent key naming with `ConfigKeyGenerator` to achieve sub-millisecond latency and cluster-wide consistency.**

CoSky is a high-performance, reactive configuration service that leverages Redis as its storage backend. Implementing CoSky Redis configuration best practices for performance and reliability ensures your distributed applications benefit from atomic updates, reactive scalability, and fault-tolerant consistency across the entire cluster.

## Architectural Foundation for CoSky Redis Configuration

CoSky stores configuration data using a **reactive, Lua-script-driven** architecture that guarantees atomic updates and efficient change-notification. Understanding these core components is essential for optimizing your deployment:

- **`RedisConfigService`** ([[`RedisConfigService.kt`](https://github.com/ahoo-wang/cosky/blob/main/RedisConfigService.kt)](https://github.com/ahoo-wang/cosky/blob/main/cosky-config/src/main/kotlin/me/ahoo/cosky/config/redis/RedisConfigService.kt)): The primary CRUD API that executes Lua scripts for **set**, **remove**, and **rollback** operations, ensuring server-side atomicity.

- **`RedisConsistencyConfigService`** ([[`RedisConsistencyConfigService.kt`](https://github.com/ahoo-wang/cosky/blob/main/RedisConsistencyConfigService.kt)](https://github.com/ahoo-wang/cosky/blob/main/cosky-config/src/main/kotlin/me/ahoo/cosky/config/redis/RedisConsistencyConfigService.kt)): Wraps the delegate service with a **local in-memory cache** (default TTL 1 minute) and listens to Redis Pub/Sub events to invalidate stale entries.

- **`ConfigRedisScripts`** ([[`ConfigRedisScripts.kt`](https://github.com/ahoo-wang/cosky/blob/main/ConfigRedisScripts.kt)](https://github.com/ahoo-wang/cosky/blob/main/cosky-config/src/main/kotlin/me/ahoo/cosky/config/redis/ConfigRedisScripts.kt)): Loads the three critical Lua scripts ([[`config_set.lua`](https://github.com/ahoo-wang/cosky/blob/main/config_set.lua)](https://github.com/ahoo-wang/cosky/blob/main/cosky-config/src/main/resources/config_set.lua), [[`config_remove.lua`](https://github.com/ahoo-wang/cosky/blob/main/config_remove.lua)](https://github.com/ahoo-wang/cosky/blob/main/cosky-config/src/main/resources/config_remove.lua), [[`config_rollback.lua`](https://github.com/ahoo-wang/cosky/blob/main/config_rollback.lua)](https://github.com/ahoo-wang/cosky/blob/main/cosky-config/src/main/resources/config_rollback.lua)) that perform compare-and-set logic on the server side.

- **`ConfigKeyGenerator`** ([[`ConfigKeyGenerator.kt`](https://github.com/ahoo-wang/cosky/blob/main/ConfigKeyGenerator.kt)](https://github.com/ahoo-wang/cosky/blob/main/cosky-config/src/main/kotlin/me/ahoo/cosky/config/ConfigKeyGenerator.kt)): Centralizes the naming scheme for all Redis keys (`cfg_idx`, `cfg_htr_idx`, `cfg_htr`, `cfg`), ensuring consistent prefixes for monitoring and TTL policies.

- **`RedisConfigEventListenerContainer`** ([[`RedisConfigEventListenerContainer.kt`](https://github.com/ahoo-wang/cosky/blob/main/RedisConfigEventListenerContainer.kt)](https://github.com/ahoo-wang/cosky/blob/main/cosky-config/src/main/kotlin/me/ahoo/cosky/config/redis/RedisConfigEventListenerContainer.kt)): Subscribes to Redis channels (`cfg:*`) and forwards change events to the consistency service, ensuring all application instances stay synchronized.

## Performance Optimization Strategies

### Leverage Lua Scripts for Atomic Operations

Always use the provided Lua scripts via `RedisConfigService` rather than client-side `MULTI/EXEC` blocks. The [[`config_set.lua`](https://github.com/ahoo-wang/cosky/blob/main/config_set.lua)](https://github.com/ahoo-wang/cosky/blob/main/cosky-config/src/main/resources/config_set.lua) script executes hash checks, version increments, and history trimming in a single server-side operation with **O(1)** latency:

```lua
local configKey = KEYS[1] .. ':' .. ARGV[1]
local data = ARGV[2]
local hash = ARGV[3]

if redis.call('EXISTS', configKey) == 1 then
    local curHash = redis.call('HGET', configKey, 'hash')
    if curHash ~= false and curHash ~= hash then
        return false   -- conflict
    end
end

redis.call('HMSET', configKey,
    'data', data,
    'hash', hash,
    'version', tonumber(redis.call('HINCRBY', configKey, 'version', 1))
return true

```

### Tune the Reactive Client Connection Pool

Configure `ReactiveStringRedisTemplate` with a properly sized connection pool to prevent thread starvation under load. For Lettuce (the default reactive driver), set `lettuce.pool.max-active` and `max-idle` based on your CPU and IO capacity, and enable client-side timeouts (e.g., 2 seconds) for fast back-pressure handling.

### Optimize Local Cache TTL

`RedisConsistencyConfigService` maintains a local in-memory cache with a default TTL of **1 minute**. For read-heavy workloads, increase this to 5 minutes to reduce Redis load. For environments with frequent config changes, decrease it to 10-30 seconds to improve consistency. Adjust via the `CONFIG_CACHE_TTL` field or Spring configuration properties.

### Implement Consistent Key Design and Memory Policies

Adhere strictly to the prefixes defined in `ConfigKeyGenerator` (`cfg_idx`, `cfg_htr_idx`, `cfg_htr`, `cfg`). Avoid adding dynamic segments to key names. On the Redis server, set `maxmemory` with an eviction policy like `volatile-lru` (evicts only keys with TTL) or `allkeys-lru` if you accept eviction of configuration data. Keep the `CONFIG_IDX` and `CONFIG_HTR_IDX` sets small, as they contain only IDs.

## Reliability and Consistency Patterns

### Pub/Sub Cache Invalidation

Deploy `RedisConfigEventListenerContainer` on each application node to subscribe to the `cfg:*` channel pattern. When `RedisConfigService` publishes a change event, all nodes receive the notification and invalidate their local caches, ensuring **eventual consistency** across the cluster without polling Redis.

### History Management and Rollback

CoSky maintains configuration history using a capped sorted set controlled by `ConfigRollback.HISTORY_SIZE` (default **30**). Each update pushes a new version to `cfg_htr_idx` and trims old entries. Adjust this value based on compliance requirements, but avoid setting it excessively high, as each version adds a sorted-set entry that impacts `ZRANGE` performance.

### Cluster Deployment with Hash Tags

When using Redis Cluster, ensure all related keys (index, history, and config) reside in the same slot by using **hash tags**. Wrap the namespace in curly braces: `{myNs}:cfg:myConfig`. This is required because CoSky's Lua scripts operate on multiple keys, and Redis mandates that all keys accessed by a script must be in the same slot for atomic execution.

## Deployment Checklist

1. **Configure Redis Server** – Set `maxmemory`, choose `volatile-lru` or `allkeys-lru` eviction policy, enable AOF with `fsync=everysec`, and activate `notify-keyspace-events` for `K$` (keyspace) and `E$` (expired).

2. **Set up Connection Pool** – In [`application.yml`](https://github.com/ahoo-wang/cosky/blob/main/application.yml), configure `lettuce.pool.max-active` and `max-idle` based on your throughput requirements, and set client timeouts to 2 seconds.

3. **Validate Lua Scripts** – Run `SCRIPT LOAD` for each script and confirm the SHA1 matches the values used by `ConfigRedisScripts` to ensure server-side execution.

4. **Enable Pub/Sub** – Verify that the application can subscribe to the `cfg:*` pattern using `redis-cli PSUBSCRIBE cfg:*` and that `RedisConfigEventListenerContainer` starts without errors.

5. **Apply Cache TTL** – Override the default 1-minute TTL in `RedisConsistencyConfigService` via Spring configuration if your workload requires different consistency windows.

6. **Run Integration Tests** – Execute `./gradlew :cosky-config:test` to validate all Redis interactions, including Lua script execution and Pub/Sub events.

7. **Monitor Metrics** – Add dashboards for `cmdstat_eval`, `keyspace_hits/misses`, `used_memory`, and `expired_keys`, with alerts on script latency >100ms or memory usage >80%.

## Code Examples

### Registering the Reactive Redis Template

Configure the `ReactiveStringRedisTemplate` as a Spring Bean to enable non-blocking Redis operations:

```kotlin
@Bean
fun reactiveStringRedisTemplate(
    factory: ReactiveRedisConnectionFactory
): ReactiveStringRedisTemplate = ReactiveStringRedisTemplate(factory)

```

### Using RedisConfigService for Atomic Updates

Perform atomic configuration updates with automatic conflict detection:

```kotlin
val configService = RedisConfigService(reactiveStringRedisTemplate)

// Store config with hash-based optimistic locking
val setResult = configService
    .setConfig("myNamespace", "myConfig", """{"key":"value"}""")
    .block()   // block only for demo; use reactive chaining in production

println("Config set successfully: $setResult")

```

### Configuring the Consistency Wrapper with Custom TTL

Enable local caching with a custom TTL for read-heavy workloads:

```kotlin
@Bean
fun redisConsistencyConfigService(
    delegate: ConfigService,
    listenerContainer: ConfigEventListenerContainer
): ConfigService = RedisConsistencyConfigService(
    delegate = delegate,
    configEventListenerContainer = listenerContainer,
    hookOnResetCache = { event -> logger.info("Cache reset for ${event.namespacedConfigId}") }
).apply {
    // Extend cache TTL to 5 minutes for high-read scenarios
    val field = RedisConsistencyConfigService::class.java.getDeclaredField("CONFIG_CACHE_TTL")
    field.isAccessible = true
    field.set(null, Duration.ofMinutes(5))
}

```

### Publishing Configuration Changes

Trigger cache invalidation across all nodes by publishing change events:

```kotlin
val topic = ConfigEventTopic("myNamespace", "myConfig")
configEventListenerContainer.publish(topic, ConfigChangedEvent(...))

```

## Summary

- **Atomicity through Lua**: Always use `RedisConfigService` with server-side Lua scripts ([`config_set.lua`](https://github.com/ahoo-wang/cosky/blob/main/config_set.lua), [`config_remove.lua`](https://github.com/ahoo-wang/cosky/blob/main/config_remove.lua)) to eliminate race conditions and ensure O(1) latency.
- **Consistent Key Design**: Adhere to `ConfigKeyGenerator` prefixes and use hash tags (`{namespace}`) in Redis Cluster to keep related keys in the same slot.
- **Tiered Caching Strategy**: Deploy `RedisConsistencyConfigService` with tuned TTL (default 1 minute) and Pub/Sub invalidation to balance read performance with consistency.
- **Resource Management**: Cap history size via `ConfigRollback.HISTORY_SIZE` (default 30), configure `maxmemory` with `volatile-lru`, and size connection pools to match throughput.
- **Observability**: Monitor `cmdstat_eval`, keyspace hits/misses, and memory usage; alert on script latency >100ms.

## Frequently Asked Questions

### How does CoSky ensure atomic configuration updates in Redis?

CoSky uses server-side Lua scripts loaded via `ConfigRedisScripts` to perform compare-and-set operations atomically. The [`config_set.lua`](https://github.com/ahoo-wang/cosky/blob/main/config_set.lua) script checks the current hash, increments the version, and updates the data in a single execution, eliminating race conditions that occur with client-side `WATCH/MULTI/EXEC` blocks.

### What is the optimal cache TTL setting for RedisConsistencyConfigService?

The default **1-minute TTL** in `RedisConsistencyConfigService` suits most workloads, but you should adjust it based on your consistency requirements. For read-heavy applications with infrequent changes, increase the TTL to **5 minutes** to reduce Redis load. For environments requiring near-real-time consistency, decrease it to **10-30 seconds** and ensure `RedisConfigEventListenerContainer` is active for Pub/Sub invalidation.

### Why must I use hash tags when deploying CoSky with Redis Cluster?

CoSky's Lua scripts operate on multiple keys simultaneously (configuration data, index sets, and history sorted sets). Redis Cluster requires that all keys accessed by a Lua script reside in the same hash slot. By wrapping the namespace in curly braces (e.g., `{myNs}:cfg:myConfig`), you ensure that `ConfigKeyGenerator` produces keys that hash to the same slot, preventing `CROSSSLOT` errors and maintaining atomic execution.

### How do I monitor CoSky's Redis performance in production?

Monitor the following metrics to ensure optimal performance: track `cmdstat_eval` for Lua script latency (alert if >100ms), watch `keyspace_hits` versus `keyspace_misses` to validate cache efficiency, and monitor `used_memory` and `expired_keys` to prevent OOM conditions. Additionally, enable Spring Boot actuator metrics for `reactor.redis.*` to track connection pool health and reactive pipeline throughput.