본문으로 건너뛰기
cccskills
무료GitHub에 공개

simba

Guide for using Simba 5, the JVM leader-election and distributed-lock library, in applications. Use when adding leader election, leader-only work (runWhileLeader, schedule, @SimbaScheduled), partitioned work (group, @SimbaPartitioned), distributed locks (lock / LockTerm), configuring the JDBC (MySQL, PostgreSQL), Redis or Zookeeper backend, integrating with Spring Boot, using fencing tokens, tuning session timing, or migrating from Simba 4.x. For test-focused work, use the simba-testing skill as well.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(3)

  • SKILL.md8.2 KB
  • agents/openai.yaml185 B
  • references/backend-internals.md4.2 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Simba 5 — Sessions, Leases and Terms

Simba gives JVM applications leader election and distributed locks over a backend they already run: MySQL 8 or PostgreSQL 14+ (simba-jdbc), Redis 7 (simba-redis + simba-spring-redis) or Zookeeper (simba-zookeeper). Java 21+. Full docs: https://simba.ahoo.me/.

Model in Five Lines

  1. One Simba client per process opens one session on the backend and keeps it alive every renewInterval.
  2. Elections, locks and partition groups contend for leases (names of 1–128 chars [A-Za-z0-9._:-]) attached to that session; the session expiring frees them all.
  3. Winning a lease gives a Term: name, token (fencing token, strictly increasing per name), isValid, remaining(), ensureValid(min), revoked.
  4. Every onElected(term) is followed by exactly one onRevoked(term, reason).
  5. A term is a weakly consistent view. Never promise "no split-brain": a paused process can still write after its term ended. Resources that must reject stale writers check term.token (accept >= highest, reject lower, atomically with the write).

How to Use This Skill

Identify:

  1. The backend the project already runs (JDBC, Redis or Zookeeper). Any is fine; for strict fencing prefer JDBC or Zookeeper (Redis tokens can go back after a failover).
  2. The pattern: callbacks (election.start), work per term (runWhileLeader), periodic leader work (schedule, @SimbaScheduled), partitions (group, @SimbaPartitioned), or a lock around a step (lock).
  3. Spring Boot or plain JVM.

Read references/backend-internals.md only to debug backend behavior or explain storage, keys and paths.

Dependencies

implementation(platform("me.ahoo.simba:simba-bom:${simbaVersion}"))
implementation("me.ahoo.simba:simba-spring-boot-starter")

// one backend:
implementation("me.ahoo.simba:simba-jdbc")              // + spring-boot-starter-jdbc + driver; create tables once
implementation("me.ahoo.simba:simba-spring-redis")      // + spring-boot-starter-data-redis
implementation("me.ahoo.simba:simba-zookeeper")         // + simba.zookeeper.connect-string

JDBC tables: run me/ahoo/simba/jdbc/init-simba-mysql.sql or init-simba-postgresql.sql (on the simba-jdbc classpath) once, e.g. via spring.sql.init.schema-locations. With several backend modules, set simba.backend=jdbc|redis|zookeeper.

Spring Boot

The starter creates one Simba bean. Properties:

simba:
  backend: redis                 # only with several backend modules
  session:
    lease-duration: 15s          # failover bound after a crash
    renew-interval: 5s           # default lease/3, at most lease/2
    drift: 0.01
    margin: 50ms
  jdbc:
    dialect: mysql               # detected when unset
  redis:
    wait-replicas: 0             # >0: WAIT for replicas on new terms (not on Cluster)
  zookeeper:
    connect-string: localhost:2181

Leader-only and partitioned methods:

@SimbaScheduled(name = "report", fixedDelay = "1m")          // method () or (Term)
public void generate(Term term) { reportService.generate(term.getToken()); }

@SimbaPartitioned(group = "billing", partitions = 16, fixedDelay = "1m")   // method (Term, int)
public void bill(Term term, int partition) { billing.run(partition, term.getToken()); }

Exactly one of fixedDelay / fixedRate; initialDelay counts from each term's start. Names must be unique per application. Actuator: endpoint simba (snapshot) and health indicator simba (DOWN when the session was not live for longer than the lease). Micrometer: simba.session.renew, simba.session.state, simba.leadership, simba.locks.held, simba.revocations, simba.work.

Plain JVM

val simba = Simba.builder()
    .backend(JdbcLeaseBackend.mysql(dataSource))          // JdbcLeaseBackend.postgres(...); statement timeout 5 s
    // .backend(RedisLeaseBackend(SpringRedisScripting(connectionFactory)))
    // .backend(ZookeeperLeaseBackend("zk:2181"))
    .config(SimbaConfig(leaseDuration = Duration.ofSeconds(15)))
    .listener { event -> log.debug { "$event" } }        // SimbaEventListener
    .build()
// simba.close() revokes every term and frees every lease at once; close the backend afterwards.

Patterns

Callbacks

val election = simba.election("dispatcher")
election.start(object : LeadershipListener {
    override fun onElected(term: Term) = dispatcher.start(term.token)
    override fun onRevoked(term: Term, reason: RevokeReason) = dispatcher.stop()
})
election.stepDown(Duration.ofSeconds(30))   // give up, stay out for 30 s, keep contending afterwards
election.stop().toCompletableFuture().join() // completes after the last onRevoked

Callbacks run serially per election; keep them short. start twice for the same name on one client throws.

Work per term

val task = simba.election("ingest").runWhileLeader { term ->
    while (term.isValid) sink.write(source.next(), fencingToken = term.token)
}
task.close()   // stop: interrupt work, wait for it (max term.remaining()), release, onRevoked

Schedule

simba.election("report").schedule(Schedule.fixedRate(Duration.ofMinutes(1))) { term ->
    report.generate(fencingToken = term.token)
}

One virtual thread per term, ensureValid() before each run, failures logged and published, loop continues.

Partitions

simba.group("billing", 16).schedule(Schedule.fixedDelay(Duration.ofMinutes(1))) { term, partition -> ... }

Members are the group's live sessions; rendezvous hashing assigns partitions; partition p is lease billing.p. Every member must use the same partition count.

Lock

simba.lock("invoice-42").acquire(Duration.ofSeconds(5)).use { term ->   // LockTimeoutException on timeout
    term.ensureValid(Duration.ofSeconds(2))
    store.write(invoice, fencingToken = term.token)
}
simba.lock("invoice-42").tryAcquire()?.use { ... }                    // one attempt, null if held

LockTerm is the credential, not thread-bound; closing releases. A lock lost early (deadline, expiry, take-over) is released for good, never re-acquired.

Rules to Follow

  • Pass term.token to every write on a shared resource; check it atomically there (update ... where token <= ?).
  • Work must stop on interruption or when term.isValid turns false; ignoring interruption delays handover.
  • Use term.ensureValid(expectedStepDuration) before long steps.
  • One Simba per process; several only to isolate lease groups from each other's renewal failures.
  • Never read a term from shared state: use the Term passed to the callback or work block.
  • Do not use the same name for a lock and an election (they share one lease).

Timing

SimbaConfig(leaseDuration = 15s, renewInterval = lease/3, drift = 0.01, margin = 50ms, retryJitter = renew/2, minBackoff = 100ms). Local deadline = sentAt + remaining × (1 − drift) − margin. Constraints: renewInterval × 2 ≤ leaseDuration and leaseDuration × (1 − drift) − margin > renewInterval. Crash failover ≈ leaseDuration + next candidate attempt (renewInterval / 2 + jitter); clean stop on Redis/Zookeeper is taken over at once.

Migrating from 4.x

Not compatible (API, properties, storage): stop every 4.x node before starting 5.0. MutexContender → LeadershipListener; SimbaLocker → lock(name); SimbaScheduler / @SimbaScheduled(mutex) → election.schedule / @SimbaScheduled(name) with Term; SimbaPartitionedScheduler → group(...) / @SimbaPartitioned; relinquish → stepDown; ContendObserver → SimbaEventListener; ttl/transition → simba.session.*. On JDBC drop the 4.x simba_member table before running the 5.x script. Guide: https://simba.ahoo.me/guide/migration.

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Guide for testing code built on Simba 5 (leader election and distributed locks). Use when writing or reviewing tests for LeadershipListener callbacks, runWhileLeader / schedule work, partition groups, locks, @SimbaScheduled / @SimbaPartitioned beans, fencing-token resources (FencingSpec), LeaseBackend implementations (LeaseBackendSpec), or deterministic failover tests with InMemoryLeaseBackend and VirtualScheduler.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

Ahoo-Wang/Simba332026년 10월 19일 업데이트

Ahoo-Wang의 스킬 모두 보기

이 스킬의 문제 신고