跳到正文
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(原文)

安装前可以查看交给 AI 代理的指令内容。

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 的全部技能

报告此技能的问题