Hazelcast 캐시 통합
최신 안정판 Bluetape4k 1.11.0 릴리스 기준
제공하는 기능
섹션 제목: “제공하는 기능”bluetape4k-cache-hazelcast는 이미 연결된 HazelcastInstance를 JCache, 함수 결과 memoizer, Caffeine L1과 Hazelcast IMap L2를 묶은 Near Cache로 사용할 수 있게 합니다. 동기 API와 CompletableFuture, coroutine API를 함께 제공하며 IMap entry event로 각 JVM의 L1을 무효화합니다.
이 모듈은 Hazelcast cluster를 만들거나 소유하지 않습니다. 연결, 인증, member discovery, backup, map TTL과 직렬화는 애플리케이션의 Hazelcast 설정이 맡고, 이 모듈은 전달받은 instance와 map 위에 캐시 계약을 얹습니다.
사용하기 전에 결정할 것
섹션 제목: “사용하기 전에 결정할 것”- 표준 JCache가 필요한지,
IMap을 직접 쓰는 Near Cache·memoizer가 필요한지 고릅니다. - 단일 JVM 캐시라면
bluetape4k-cache-core의 Caffeine helper가 더 작습니다. - Redis cache가 아니라 Hazelcast data grid와 같은 key 공간을 공유해야 할 때 선택합니다.
- Near Cache key는
String으로 고정됩니다. memoizer와 JCache는 generic key를 받습니다. - value가 cluster의 Hazelcast serialization 설정으로 읽고 쓸 수 있는지 먼저 확인합니다.
- L1의 짧은 stale window를 허용할 수 있는지, listener 등록·해제와 cluster 장애를 어떻게 관측할지 정합니다.
HazelcastInstance를 시작하고 종료할 소유자는 애플리케이션입니다.
의존성 추가
섹션 제목: “의존성 추가”사용자는 Hazelcast와 하위 bluetape4k 모듈 버전을 따로 맞추지 않고 중앙 BOM 버전만 관리합니다.
dependencies { implementation(platform("io.github.bluetape4k:bluetape4k-dependencies:<version>")) implementation("io.github.bluetape4k:bluetape4k-cache-hazelcast")}Gradle project path는 :bluetape4k-cache-hazelcast, source directory는 cache/cache-hazelcast입니다. Hazelcast cluster와 client/member 설정은 애플리케이션 실행 환경에서 준비합니다.
첫 Near Cache
섹션 제목: “첫 Near Cache”val hazelcast: HazelcastInstance = applicationHazelcastClient()
val users = HazelcastCaches.nearCache<User>(hazelcast) { cacheName = "users-v1" maxLocalSize = 10_000 frontExpireAfterWrite = Duration.ofMinutes(10) recordStats = true}
try { users.put("42", user) check(users.get("42") == user)} finally { users.close() // listener와 Caffeine L1 정리 // hazelcast.shutdown()은 애플리케이션 소유자가 수행}put은 L1을 먼저 바꾼 뒤 IMap.set을 호출합니다. backend write가 실패하면 이미 바뀐 L1이 남을 수 있으므로 이 순서를 database write-through나 원자적 dual write로 해석하면 안 됩니다.
API 선택 지도
섹션 제목: “API 선택 지도”| 필요한 작업 | 시작할 API | 기억할 경계 |
|---|---|---|
| Hazelcast JCache | HazelcastJCaching, HazelcastCaches.jcache | 전달받은 instance로 manager를 찾으며 SPI 자동 탐색을 쓰지 않습니다. |
| coroutine JCache | HazelcastSuspendJCache | ICache async API를 우선하고 나머지는 Dispatchers.IO에서 실행합니다. |
| 함수 결과 공유 | HazelcastMemoizer, AsyncHazelcastMemoizer, SuspendHazelcastMemoizer | same-key 계산 병합은 한 JVM의 inFlight map 범위입니다. |
| 동기 Near Cache | HazelcastNearCache | Caffeine L1과 String-key IMap L2를 조합합니다. |
| coroutine Near Cache | HazelcastSuspendNearCache | 일부 bulk·조건부 명령은 Dispatchers.IO, async 명령은 await를 사용합니다. |
| JCache 기반 L1/L2 | HazelcastCaches.nearJCache, suspendNearJCache | factory는 listener 직렬화 실패를 피하려고 listener 없이 구성합니다. |
| retry·fallback | withResilience | cache-core의 decorator이며 Hazelcast 전용 write-behind 구현은 아닙니다. |
학습 경로
섹션 제목: “학습 경로”아래 장은 1.11.0 배포 소스와 실행 가능한 테스트를 따라 instance 소유권, async 경계, 동시 계산, L1/L2 순서와 listener 직렬화 실패를 설명합니다. 각 장에서 정상 경로뿐 아니라 실패 뒤 어느 계층이 바뀌어 있는지도 확인합니다.
- JCache와 HazelcastInstance 소유권 — provider 선택, manager 생성, cache 이름과 instance 수명주기를 정리합니다.
- Suspend JCache와 async 경계 —
ICacheunwrap,await, IO fallback과 비원자적getAndPut을 다룹니다. - IMap memoizer와 동시 계산 — sync·future·suspend 실행 위치, JVM 내 병합과 cluster 경쟁을 설명합니다.
- IMap Near Cache와 무효화 — Caffeine L1,
IMapL2, entry listener, write 순서와 통계를 따라갑니다. - JCache Near Cache와 직렬화 제한 — listener factory 직렬화 실패와 listener-free factory의 일관성 한계를 확인합니다.
- 설정, 장애, 테스트와 생태계 — 설정 validation, 종료, 운영 점검과 backend 선택 기준을 정리합니다.
JCache가 목적이면 1→2→5, native Near Cache가 목적이면 4→6, 함수 결과 공유가 목적이면 3→6 순서로 읽습니다.
권장 패턴
섹션 제목: “권장 패턴”cache 이름과 value serialization schema를 하나의 배포 계약으로 관리합니다. value 형식을 바꿀 때는 cluster의 serializer 호환성을 확인하고 필요하면 새 map/cache 이름으로 전환합니다. 원본 저장소를 읽은 뒤 cache를 채우고, database 변경이 commit된 뒤 관련 key를 갱신하거나 지웁니다.
Near Cache는 원격 변경 이벤트가 도착할 때까지 잠시 오래된 값을 돌려줄 수 있습니다. 이 시간이 허용되지 않는 값은 L1에 두지 않습니다. Hazelcast 장애 fallback을 붙일 때도 miss가 원본 저장소 장애 증폭기가 되지 않도록 동시 evaluator 수와 원본 조회 제한을 함께 둡니다.
공통 JCache·Near Cache·resilience 계약은 bluetape4k-cache-core에서 설명합니다. Redis가 운영 표준이라면 RESP3 invalidation을 쓰는 bluetape4k-cache-lettuce와 Redisson 분산 객체를 쓰는 bluetape4k-cache-redisson을 비교합니다.
Hazelcast는 IMap과 entry event를 이미 쓰는 data-grid 애플리케이션에 자연스럽습니다. cache operation을 database write-through라고 부르기 전에 repository loader/writer 경계를 별도로 설계하고, bluetape4k-exposed와 Exposed Workshop의 database cache 전략을 이어서 봅니다.
HazelcastNearCacheConfig 기본값은 이름 hazelcast-near-cache, L1 최대 10,000개, 쓰기 후 30분 만료, 접근 후 만료 없음, 통계 비활성입니다. 이름은 공백일 수 없고 용량과 duration은 0보다 커야 합니다.
이 설정은 Caffeine L1만 구성합니다. IMap의 backup, TTL, max-idle, in-memory format과 serializer는 Hazelcast Config 또는 ClientConfig에서 별도로 관리합니다. 모듈은 독자적인 value codec이나 wire format을 추가하지 않습니다.
실패 동작
섹션 제목: “실패 동작”native Near Cache의 put, putAll, remove는 L1을 먼저 바꿉니다. 이어지는 IMap 호출이 실패하면 로컬 상태와 cluster 상태가 다를 수 있습니다. replace와 성공한 putIfAbsent는 backend 결과를 확인한 뒤 L1을 갱신합니다. getAndRemove와 getAndReplace는 여러 호출을 조합하므로 단일 원자 연산이 아닙니다.
JCache Near Cache의 직접 listener-backed factory는 Caffeine front cache를 담은 listener factory를 cluster로 직렬화하다 HazelcastSerializationException으로 실패합니다. HazelcastCaches factory는 listener를 빼서 read/write는 가능하지만 peer L1 무효화를 보장하지 않습니다.
L1 hit·miss·eviction·size, IMap hit·miss, entry event 지연, client reconnect와 operation latency를 함께 봅니다. recordStats=false이면 Caffeine hit·miss는 0으로 보일 수 있습니다. memoizer에서는 evaluator latency·failure·hot key와 같은 key의 cluster 중복 계산을 관찰합니다.
close는 Near Cache listener와 L1을 정리할 뿐 IMap 데이터나 HazelcastInstance를 종료하지 않습니다. clearAll은 공유 IMap 전체를 비우므로 cache 이름을 다른 기능과 재사용하지 않습니다.
테스트
섹션 제목: “테스트”모듈 테스트는 Hazelcast Testcontainers를 사용하므로 다른 heavy database suite와 병렬로 실행하지 않습니다.
./gradlew :bluetape4k-cache-hazelcast:test --no-build-cache --no-configuration-cacheNear Cache 테스트는 CRUD, bulk, 통계와 중복 close를, memoizer 테스트는 same-key 경쟁과 실패 뒤 재계산을 검증합니다. JCache Near Cache 테스트는 listener-backed 생성이 직렬화 오류로 실패하고 factory의 listener-free read/write가 동작하는 현재 capability를 고정합니다.
워크숍
섹션 제목: “워크숍”HazelcastCachesTest에서 factory별 최소 생성을, HazelcastNearCacheTest와 HazelcastSuspendNearCacheTest에서 L1 miss·bulk·clear 경계를 실습할 수 있습니다. HazelcastNearJCacheTest는 성공 예제가 아니라 지원하지 않는 listener 조합을 확인하는 회귀 테스트도 포함합니다.
database cache-aside와 loader/writer를 함께 실습하려면 Exposed Workshop과 bluetape4k-workshop으로 이어갑니다.
1.11.0 범위
섹션 제목: “1.11.0 범위”이 매뉴얼은 release commit 6187173b58e8b4c5c435c145e00e94708f31ef75의 1.11.0 소스와 테스트를 기준으로 합니다. META-INF/services/javax.cache.spi.CachingProvider에는 provider class가 등록되지 않았으며 HazelcastJCaching이 HazelcastCachingProvider를 명시적으로 생성합니다.
README에 남아 있는 ResilientHazelcastNearCache와 전용 write-behind queue 구현은 이 release source에 없습니다. resilience가 필요하면 cache-core의 withResilience decorator 범위에서 사용합니다. factory가 만든 JCache Near Cache는 listener-free degraded mode이며 peer front-cache propagation을 제공하지 않습니다.
Source와 tests
섹션 제목: “Source와 tests”HazelcastCaches.ktHazelcastJCaching.ktHazelcastSuspendJCache.ktHazelcastMemoizer.ktHazelcastNearCache.ktHazelcastEntryEventListener.ktHazelcastNearJCacheTest.ktHazelcastSuspendNearCacheTest.kt
배포본 다이어그램
섹션 제목: “배포본 다이어그램”아래 그림은 1.11.0 배포본의 README 자산을 해당 배포 커밋에서 직접 불러옵니다. 이후 SNAPSHOT이 아니라 이 매뉴얼 버전의 구조와 실행 흐름을 보여 줍니다. 미리보기를 누르면 같은 배포 커밋의 SVG 원본이 열립니다.
HazelcastNearCache 클래스 계층도
섹션 제목: “HazelcastNearCache 클래스 계층도”배포본 README: cache/cache-hazelcast/README.ko.md
HazelcastNearCache 런타임 처리 흐름
섹션 제목: “HazelcastNearCache 런타임 처리 흐름”배포본 README: cache/cache-hazelcast/README.ko.md
IMap EntryListener Invalidation 다이어그램
섹션 제목: “IMap EntryListener Invalidation 다이어그램”배포본 README: cache/cache-hazelcast/README.ko.md


