콘텐츠로 이동

Bluetape4k Leader Part 5: Backend, 운영 기능, Benchmark

Robotic builders arranging Redis, etcd, SQL, Kubernetes, and observability props on a leader election storage workbench
백엔드를 바꾸면 API보다 운영 환경이 먼저 달라집니다. lease, TTL, 연장, 관측은 모두 저장소 선택의 일부입니다.

Part 4에서는 Spring Boot annotation/AOP와 Ktor application lifecycle 쪽 경계를 봤습니다. Part 5에서는 한 층 더 내려갑니다. 같은 runIfLeader라도 Redis에 저장할지, etcd lease로 잡을지, SQL row로 남길지, Kubernetes Lease로 운영할지에 따라 장애 대응과 관측 방법이 달라집니다.

이 선택은 “어느 client API가 더 예쁜가”보다 운영 판단에 가깝습니다. leader election은 평소에는 조용해야 하고, 장애가 났을 때는 누가 왜 실행했는지 설명할 수 있어야 합니다.

먼저 운영 중인 인프라의 재사용성을 검토한다

섹션 제목: “먼저 운영 중인 인프라의 재사용성을 검토한다”
Decision map for choosing leader election storage backends by operation shape
backend-picker는 성능 순위표가 아니라 이미 운영 중인 기반에서 출발하는 선택 가이드입니다.

처음 선택은 대부분 이 질문으로 충분합니다.

지금 운영 중인 substrate먼저 볼 backend이유
Redis가 공통 cache/lock substrate다Lettuce 또는 RedissonRedis TTL과 token ownership으로 단일 leader를 만들기 쉽습니다. Lettuce는 가벼운 client path, Redisson은 lock 친화적인 운영 경험이 장점입니다.
etcd가 control-plane truth다etcd v3lease와 conditional write가 reconciler/control-plane 작업과 잘 맞습니다.
DB 운영팀이 장애 대응 범위를 관리한다Exposed JDBC/R2DBCrow, token, 만료 시각을 SQL로 관찰하고 migration/audit과 붙이기 쉽습니다.
workload가 Kubernetes 안에서 돈다Kubernetes Leasecoordination.k8s.io/v1 Lease와 pod/operator lifecycle을 그대로 씁니다.
이미 MongoDB, DynamoDB, Consul, Hazelcast, ZooKeeper가 있다해당 backend새 인프라를 늘리지 않고 기존 운영 지식으로 leader lock을 관리할 수 있습니다.

backend-picker를 이렇게 잡는 이유는 간단합니다. leader election backend는 성능 숫자만으로 고르기 어렵습니다. 장애 시 누가 lease를 연장할 수 있는지, 누가 token을 검증하는지, 만료된 owner를 어떻게 정리하는지, 운영자가 어디서 상태를 확인하는지가 함께 따라옵니다.

Redis: Lettuce와 Redisson을 우선 검토한다

섹션 제목: “Redis: Lettuce와 Redisson을 우선 검토한다”

Redis는 가장 자연스러운 기본값입니다. 이미 Redis를 cache, rate limit, short-lived coordination에 쓰고 있다면 leader lock도 같은 운영 환경에서 볼 수 있습니다.

bluetape4k-leader는 Redis 계열을 두 가지로 제공합니다.

Backend잘 맞는 상황주의점
leader-redis-lettuceLettuce client를 직접 운영하고, Kotlin coroutine/suspend path까지 가볍게 가져가고 싶을 때lease TTL, token 검증, auto-extension을 애플리케이션 계약으로 이해해야 합니다.
leader-redis-redissonRedisson을 이미 쓰고 있고 lock API/운영 경험이 익숙할 때electors는 명시적 leaseTime을 넘기므로 benchmark의 autoExtend는 Redisson native watchdog이 아니라 bluetape4k 공통 extender path입니다.

긴 작업에서는 autoExtend = true로 lease를 갱신할 수 있습니다. 다만 group election의 auto-extension은 아직 지원하지 않습니다. group slot은 각 slot이 별도 lease와 completion boundary를 가지기 때문입니다.

val options = LeaderElectionOptions(
leaseTime = 30.seconds,
autoExtend = true,
)
val election = RedissonLeaderElector(client, options)
election.runIfLeader("nightly-maintenance") {
LockAssert.assertLocked("nightly-maintenance")
LockExtender.extendActiveLock("nightly-maintenance", 60.seconds)
runLongJob()
}

LockAssert는 현재 코드가 실제 lock 범위 안에서 실행 중인지 확인하는 guard입니다. fail-open sentinel 범위나 lock 밖에서는 통과하지 않습니다. LockExtender는 현재 lock의 만료 시간을 늘리는 운영 도구입니다. 실패 원인은 legacy Boolean만 보면 뭉개질 수 있으므로 내부적으로는 ExtendOutcome처럼 not-held, wrong-thread, backend failure를 구분합니다.

etcd: control-plane 작업에 잘 맞는다

섹션 제목: “etcd: control-plane 작업에 잘 맞는다”

etcd는 “이미 control-plane state를 etcd에 둔다”는 팀에 잘 맞습니다. Kubernetes 밖에서 reconciler를 돌리거나, desired state를 한 node만 적용해야 하는 작업이라면 etcd lease는 자연스럽습니다.

Redis가 application substrate라면, etcd는 control-plane substrate에 가깝습니다. backend 선택 기준도 다릅니다. “우리 서비스가 Redis를 항상 먼저 붙인다”가 아니라 “이 작업의 authoritative state가 etcd에 있다”면 etcd를 보는 편이 낫습니다.

예제도 그쪽에 맞춰져 있습니다. examples/etcd-reconciler는 한 node만 desired state를 적용하는 control-plane reconciler 흐름을 보여줍니다.

나머지 lock storage는 상황에 맞게 선택한다

섹션 제목: “나머지 lock storage는 상황에 맞게 선택한다”

Redis와 etcd만 있는 것은 아닙니다. Part 5의 목적은 backend를 순위표처럼 줄 세우는 것이 아니라, 운영 substrate에 맞춰 빠르게 찾는 것입니다.

Family저장 의미읽는 법
Exposed JDBC/R2DBClock row, token, 만료 시각, optional history rowSQL로 상태를 직접 설명해야 하는 migration gate, tenant aggregation, audit-heavy batch에 좋습니다.
MongoDBfindOneAndUpdate와 TTL index 계열MongoDB가 이미 operational store이고 short-lived ownership을 document로 보고 싶을 때 맞습니다.
DynamoDBconditional item write와 TTL 성격AWS-native 운영 환경이 중요할 때 후보가 됩니다. benchmark row는 noise가 커서 반복 측정 후 판단해야 합니다.
ConsulSession + KVservice maintenance/drain처럼 Consul 운영 모델과 맞는 작업에 좋습니다.
Kubernetes Leasecoordination.k8s.io/v1 Lease objectpod/operator lifecycle과 관측을 Kubernetes API로 통일하고 싶을 때 선택합니다.
HazelcastIMap 기반 backendHazelcast cluster가 이미 runtime substrate일 때 의미가 있습니다. README는 CP Subsystem 기반이 아니라 IMap 기반이라고 명시합니다.
ZooKeeperCurator lock/semaphoreZooKeeper/Curator 운영 지식이 있고 legacy coordination substrate를 유지해야 할 때 후보입니다.

운영 기능: 연장, 검증, 기록, metric

섹션 제목: “운영 기능: 연장, 검증, 기록, metric”

backend를 고른 뒤에도 운영 질문은 남습니다.

질문도구
작업이 lease보다 길어질 수 있는가autoExtend, LeaderLeaseAutoExtender, LockExtender
지금 코드가 정말 lock scope 안에서 실행 중인가LockAssert.assertLocked() / assertLockedSuspend()
누가 leader였고, 실패했는가leader history recorder와 backend별 history sink
AOP 경계에서 몇 번 시도하고 몇 번 skip되었는가leader-micrometerMicrometerLeaderAopMetricsRecorder

history recorder는 success/completed/failed를 남기되, sink 장애가 본 작업을 망치지 않도록 best-effort로 감쌉니다. cancellation은 삼키지 않습니다. 이 원칙은 Part 4에서 본 Ktor lifecycle과도 같습니다. 취소는 취소고, 일반 실패는 기록 가능한 실패입니다.

Micrometer 쪽은 AOP 경계에서 시도, 획득, not-acquired, execution duration, task failure, active gauge를 기록합니다. 운영 dashboard에서는 lock contention과 backend failure를 같은 skip으로 뭉개지 않아야 합니다. lock contention은 정상적인 건너뛰기이고, backend failure는 신뢰성 문제입니다.

Benchmark: 숫자는 방향을 보여 주고, 해석 조건은 계약이다

섹션 제목: “Benchmark: 숫자는 방향을 보여 주고, 해석 조건은 계약이다”

benchmark README는 먼저 선을 긋습니다. 이 모듈은 같은 머신에서 before/after를 비교하기 위한 kotlinx-benchmark suite이고, release-grade performance claim이 아닙니다. 2026-05-21 baseline은 one fork, one thread, warmup 2회, 1초 measurement 3회 조건입니다. 2026-05-29에는 PostgreSQL/MySQL row가 추가됐고, 2026-06-01에는 Redis lease-extension row가 추가됐습니다.

Distributed backend throughput chart for bluetape4k-leader
Distributed backend throughput. Higher is better. Local and H2 shape rows are intentionally excluded from this chart.
Distributed backend latency chart for bluetape4k-leader
Distributed backend average time. Lower is better. PostgreSQL/MySQL exposed rows are much slower in this single-machine benchmark shape.

대표 숫자는 이렇게 읽으면 됩니다.

RowThroughput ops/sAverage time us/op해석
Lettuce Redis blocking1,454.7699.4Redis 계열은 분산 backend chart에서 상위권입니다.
Redisson Redis blocking1,415.8699.7Lettuce와 비슷한 대역입니다. 운영 client 선호가 선택에 영향을 줍니다.
Hazelcast blocking1,460.9766.3이 benchmark에서는 Redis와 같은 상위권입니다.
MongoDB blocking843.71,131.0document store 기반으로 충분히 경쟁력 있는 row입니다.
ZooKeeper blocking804.31,372.2Curator 기반 coordination substrate 후보입니다.
Consul blocking593.61,900.6Consul session/KV 모델에 맞는 운영 작업에서 봐야 합니다.
etcd suspend467.52,239.4control-plane 적합성이 성능 숫자보다 더 큰 선택 이유일 수 있습니다.
PostgreSQL/MySQL exposed rows50-80대13,000-17,000대같은 머신의 분산 DB row는 느립니다. local/H2와 직접 비교하지 않습니다.

Kubernetes Lease는 별도 benchmark source set에서 K3s Testcontainers로 측정합니다. README의 대표 row는 blocking 171.5 ops/s, suspend 164.7 ops/s입니다. Fabric8 client runtime이 기본 target의 etcd/Vert.x runtime과 달라서, 이 차이도 중요한 해석 조건입니다.

Redis lease-extension benchmark는 더 좁은 질문을 봅니다. normal runIfLeaderautoExtend row의 차이는 JMH 오차 범위 안에 있습니다. runIfLeaderWithRenewalWindow row는 90 ms lease, 45 ms action dwell 조건으로 auto-extension path를 강제로 지나가게 만든 측정입니다. 50,000 us/op50 ms/op로 읽어야 하며, 이 숫자를 “운영에서 항상 50 ms/op가 든다”로 읽으면 안 됩니다.

Part 5를 읽고 바로 코드를 보고 싶다면 예제부터 보면 됩니다.

ExampleBackend보는 포인트
examples/batch-schedulerRedis/Springcluster-wide scheduled batch
examples/migration-gateExposed JDBCschema/data migration을 한 node만 실행
examples/webhook-pollerRedispoller 중복 실행 방지
examples/cache-warmerRediscache warming job coordination
examples/tenant-aggregatorExposed R2DBCtenant별 독립 leader
examples/ktor-appKtor + LettuceLeaderElectionPluginleaderScheduled
examples/prometheus-dashboardSpring + LettuceAOP metric, Prometheus/Grafana
examples/etcd-reconcileretcddesired state reconciler
examples/consul-maintenanceConsulmaintenance/drain workflow
examples/k8s-leaseKubernetes Leaselow-level Lease acquire/release/reacquire
examples/k8s-operatorKubernetes Lease + Spring3-replica operator pattern
examples/rate-limiterLettuce + Bucket4jleader-dispatched external API probes
examples/redisson-watchdogRedissonlong-running job and shared lease extender

댓글

GitHub 계정으로 의견을 남기거나 reaction을 남길 수 있습니다.