diff --git a/README.md b/README.md
index 81b46a5..734f6e0 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,9 @@ A lib that regulates the cache-based requests an application receives in terms o
- (If link this lib with Spring) Spring Boot 2.7 (Spring Framework 5.3) or later if you are a user.
## Usage
-- Choose from the following based on your platform (There is a detailed explanation in the wiki.)
+
+Choose a module based on your platform (there is a detailed explanation in the wiki):
+
`implementation("com.linecorp.cse.reqshield:core:{version}")`
`implementation("com.linecorp.cse.reqshield:core-reactor:{version}")`
`implementation("com.linecorp.cse.reqshield:core-kotlin-coroutine:{version}")`
@@ -19,21 +21,60 @@ A lib that regulates the cache-based requests an application receives in terms o
`implementation("com.linecorp.cse.reqshield:core-spring-webflux:{version}")`
`implementation("com.linecorp.cse.reqshield:core-spring-webflux-kotlin-coroutine:{version}")`
+### Release versions
+
+Release versions are available from Maven Central. Add Maven Central and use a release version such as `1.0.0`:
+
+```kotlin
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation("com.linecorp.cse.reqshield:core:1.0.0")
+}
+```
+
+### Dev snapshot versions
+
+Dev versions are published as Maven snapshots and require the Maven Central Snapshot repository. Use a snapshot version
+such as `1.0.0-SNAPSHOT`:
+
+```kotlin
+repositories {
+ mavenCentral()
+ maven {
+ name = "MavenCentralSnapshots"
+ url = uri("https://central.sonatype.com/repository/maven-snapshots/")
+ mavenContent {
+ snapshotsOnly()
+ }
+ }
+}
+
+dependencies {
+ implementation("com.linecorp.cse.reqshield:core:1.0.0-SNAPSHOT")
+}
+```
+
+Snapshot versions can be updated without changing the version string. If Gradle continues to use a cached snapshot,
+refresh dependencies with `./gradlew build --refresh-dependencies`.
+
## Testing & Integration Tips
### Integration tests with Redis (Testcontainers)
- Redis-backed integration tests using Testcontainers always run as part of module test tasks.
- Requirements:
- - A working local Docker daemon with network access to pull `redis:6.2.7-alpine` on first run.
- - Sufficient permissions to start containers from tests.
+ - A working local Docker daemon with network access to pull `redis:6.2.7-alpine` on first run.
+ - Sufficient permissions to start containers from tests.
- If you need to temporarily bypass Redis ITs locally (e.g., no Docker), run specific unit-test-only tasks or exclude the example modules when invoking Gradle.
### WebFlux null handling
- `@ReqShieldCacheable(nullHandling = ...)` controls how `null` values are emitted in WebFlux:
- - `EMIT_EMPTY` (default): map `null` to `Mono.empty()`.
- - `ERROR`: throw an `IllegalStateException` if a `null` value is produced.
+ - `EMIT_EMPTY` (default): map `null` to `Mono.empty()`.
+ - `ERROR`: throw an `IllegalStateException` if a `null` value is produced.
### Cache key layout
@@ -53,9 +94,9 @@ A lib that regulates the cache-based requests an application receives in terms o
- Every lock acquisition carries an ownership token. Only the holder that acquired the lock can release it, so a slow
holder whose lock already expired can no longer release the lock of the next holder.
- Recommended Redis implementation:
- - Lock: `SET {lockKey} {token} NX PX {ttlMillis}` (atomic; never `SETNX` followed by a separate `PEXPIRE`)
- - Unlock: compare-and-delete in a Lua script, e.g.
- `if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end`
+ - Lock: `SET {lockKey} {token} NX PX {ttlMillis}` (atomic; never `SETNX` followed by a separate `PEXPIRE`)
+ - Unlock: compare-and-delete in a Lua script, e.g.
+ `if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end`
- The example modules contain working implementations for `RedisTemplate`, `ReactiveRedisTemplate` and the coroutine
extensions.