Codync
免费开源的 1:1 Grok Bot 与 Muse 替代方案。可在 iPhone、Mac 或 Linux 桌面上将 Claude 代码、Codex、Cursor 以及 40 多个编码代理作为持久机器人使用。支持群组聊天、线程、应用。
AI 翻译
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗A PostgreSQL-backed job scheduler for Java and Kotlin: durable steps, events and retries, with minimal dependencies, no extra infrastructure and an optional admin UI
[!NOTE] bikesched is at a super early stage (
0.2.0-SNAPSHOT) and can break at any time: APIs and the database schema may change without notice and without migrations.It is also a first experiment in building a library heavily with AI coding agents; hence the logo, a nod to Simon Willison's pelican riding a bicycle LLM benchmark.
Simon, the creator of the bikesched project: I was working on a project and looking for an open source library that does job scheduling with durable execution, has minimal dependencies and is PostgreSQL-focused, but didn't find one that fit. So instead of continuing that project, I started "bikeshedding" on a library with these properties, with AI doing much of the implementation, around the needs I'd actually run into.
bikesched stores jobs in PostgreSQL and lets every connected application instance (node) work on
them. Nodes claim due jobs with SELECT … FOR UPDATE SKIP LOCKED, so many nodes can share one
database without stepping on each other and without any coordinator besides PostgreSQL.
bikesched is inspired by some great job schedulers, like db-scheduler, JobRunr, Quartz and River, and by durable execution engines like Inngest and Restate. The goal is to keep it small and easy to drop into your app:
bikesched_*), and bikesched creates and migrates the schema on first use.One thing bikesched isn't: a workflow engine. Steps and compensation live inside a single job, so there are no process models (BPMN), no visual modelling and no orchestration across services. If that's what you're after, have a look at Camunda or similar tools.
| Module | Contents |
|---|---|
bikesched-core | the scheduler; depends on the PostgreSQL driver and the SLF4J API only; optional schema migration spec |
bikesched-jackson | JacksonJsonMapper: payloads, step results and events as records, via Jackson 3 |
bikesched-kotlin | Kotlin extensions: configuration DSL, typed handlers, kotlin.time durations |
bikesched-annotations | job handlers declared as @JobHandler methods, see Annotations |
bikesched-cron | cron expressions as CronRecurrence or as strings (schedule("0 3 * * *"), @JobHandler), via cron-utils, see Cron expressions |
bikesched-micrometer | Micrometer meters for claims, attempts and published events, see Metrics |
LISTEN/NOTIFY,
polling is only the safety netjob.postpone(…), without
using up an attemptjob.sleep(…) without holding a threadTypedJob/TypedEvent constants, next to plain string namesnullbikesched.admin()DataSource dataSource = ...; // any PostgreSQL DataSource, e.g. a HikariCP pool
try (Bikesched bikesched = Bikesched.builder(dataSource)
.nodeName("worker-1") // optional, defaults to the host name
.build()) { // migrates the schema, registers the node, starts polling
// Register a handler for a job name ...
bikesched.registerJobHandler("send-mail", job -> mailer.send(job.payload()));
// ... and schedule jobs with that name
bikesched.newJob("send-mail").payload("{\"to\": \"[email protected]\"}").schedule();
}
A handler that throws fails the attempt; the job is retried until its maximum number of attempts is
used up and then marked FAILED.
This is where bikesched stops looking like a regular job scheduler: a handler can run durable steps and wait for an event, without holding a thread or re-doing work on retries or resumes:
bikesched.registerJobHandler("order", job -> {
Order order = job.payload(Order.class);
// Creates a Stripe PaymentIntent; runs once, a retried or resumed job gets the id back instead
String paymentIntentId = job.step("create-payment-intent", String.class,
step -> stripe.createPaymentIntent(order).id());
// Suspends (no thread held) until Stripe's webhook reports on this payment intent
PaymentUpdated webhook = job.waitForEvent("await-payment", "payment-intent-updated", paymentIntentId,
PaymentUpdated.class)
.receive();
switch (webhook.status()) {
case SUCCEEDED -> job.step("mark-paid", () -> orders.markPaid(order, paymentIntentId));
case FAILED -> job.step("mark-failed", () -> orders.markFailed(order, paymentIntentId));
}
});
// in the Stripe webhook endpoint, possibly minutes or days later
bikesched.newEvent("payment-intent-updated", webhookPayload.paymentIntentId())
.payload(webhookPayload)
.publish();
A plain job scheduler would need its own tables and glue code to carry order and
paymentIntentId across that wait, to not create a second payment intent on a retry, and to
resume on another node after a crash; here it's job.step(...) and job.waitForEvent(...). See
Steps and compensation and
Events.
The documentation covers the concepts in detail:
Runnable examples with tests — a taxi booking saga with event waits and compensation, a cinema
ticket order with a StandaloneJobHandler, a webhook with a polling fallback, a provider's rate
limit with Bucket4j, chat messages deduplicated by idempotency keys, an approval with a reminder and
an escalation, a fan-out with child jobs, a recurring job on two nodes, and a Spring Boot
application whose node and handlers are wired by the starter — live in the
bikesched-examples modules.
Requirements: JDK 26 (or let Gradle provision it via the toolchain) and Docker for the tests, which run against PostgreSQL 18 with Testcontainers. The build compiles for Java 21, the oldest Java version bikesched supports (ADR 0049).
./gradlew build
The load and failure tests (database restarts, cut-off nodes,
hanging connections, throughput) take a few minutes and run on demand, before a release:
./gradlew :bikesched-stress-tests:stressTest.
To try it out locally, start PostgreSQL with Docker Compose and run the simple example, which registers a handler and runs one job:
docker compose up -d # POSTGRES_PORT=5433 docker compose up -d if 5432 is taken
./gradlew :bikesched-examples:java:runSimple # honors POSTGRES_PORT as well
All dependency and tool versions are managed in gradle/libs.versions.toml.
Releases (see the GitHub Releases) and the snapshots of main and the release branches
are published to GitHub Packages: org.bikesched:bikesched-core, bikesched-jackson,
bikesched-kotlin, bikesched-annotations, bikesched-cron, bikesched-micrometer,
bikesched-spring-boot-starter, bikesched-admin, bikesched-admin-shaded and bikesched-test,
all with the same version (0.2.0-SNAPSHOT on this branch). Reading from GitHub Packages requires a
GitHub token with the read:packages scope, even for public packages. How releases are cut:
RELEASING.md.
repositories {
maven {
url = uri("https://maven.pkg.github.com/bikesched/bikesched")
credentials {
username = providers.gradleProperty("gpr.user").get()
password = providers.gradleProperty("gpr.token").get() // token with read:packages
}
}
}
dependencies {
implementation("org.bikesched:bikesched-core:0.2.0-SNAPSHOT")
}
bikesched is licensed under the Apache License 2.0: you may use, modify and embed it, also in commercial and closed-source products. Simon, the creator of the bikesched project: it's permissive on purpose, so that anyone who runs into the same need I did can drop it into their own project, open or closed source, without license-review friction. The name and logo are trademarks, see the trademark policy for how to refer to bikesched and how to name forks.
Spring Boot auto-configuration: the node from the context, @JobHandler beans found automatically, see Spring Boot |
bikesched-admin | a small admin page and JSON API: jobs, steps, events and nodes, with retry and cancel, served by the JDK's HTTP server or any framework, see Admin UI |
bikesched-test | testing support: a test node factory, JobAwait wait helpers (Awaitility internally) and time skipping, see Testing |