本文へ移動
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.

日本語の概要は準備中です。原文の説明を表示しています。

連携ツールPostgreSQLMySQLRedis
インストール方法を見る

含まれるファイル(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 / 1000 文字

このフォームは 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 のスキルをすべて見る

このスキルの問題を報告する