Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Release

# Publishes the aggregated artifact bundle to the Maven Central (Sonatype
# Central Portal) on a version tag, e.g. `git tag v0.1.0 && git push --tags`.
# Central Portal) on a version tag, e.g. `git tag v1.0.0 && git push origin refs/tags/v1.0.0`.
#
# Required repository secrets (Settings → Secrets and variables → Actions):
# SIGNING_KEY ASCII-armored PGP private key (the whole block)
Expand Down
123 changes: 107 additions & 16 deletions AGENTS.md

Large diffs are not rendered by default.

51 changes: 50 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,55 @@
# Changelog

## Unreleased
## 1.0.0 (unreleased)

This branch prepares Threadmill 1.0.0. Publication requires the complete release
and soak qualification gates; this entry does not claim those runs have passed.
See the [compatibility and upgrade guide](docs/compatibility.md) before upgrading
from 0.3.0. Delivery remains **at least once**; handlers must be idempotent.

- Hardened execution persistence through datastore outages. Finished handlers
retain a finalizer until their outcome commits or shutdown transfers recovery
to another node. Heartbeats renew only confirmed active attempts, so a lost
claim acknowledgement cannot leave an abandoned job permanently shielded.
- Persisted retry decisions and attempt-local execution revisions. Recovery
retains the original retry policy, while delayed progress/log/check-in writes
cannot overwrite acknowledged newer diagnostics or liveness.
- Fixed concurrency-hold release for unclaimed jobs and execution cleanup across
timeout, shutdown and orphan recovery. Metrics and tracing distinguish
overlapping contexts for the same job and unwind their resources on every exit.
- Bounded atomic bulk inserts and deferred Spring enqueues to 1,000 jobs and
8 MiB of encoded bodies. New JSON jobs reserve lifecycle space; bounded
diagnostic compaction preserves identity, payload and execution policy.
- Made deferred enqueue failures observable through `AfterCommitEnqueueFailure`.
The default `after_commit` mode does not make business and job writes atomic;
use PostgreSQL `join_transaction` with the same DataSource or a durable outbox.
- Bounded maintenance and retention scans with resumable cursors. Retention
protects pending/unknown retries, waiting workflows and live deduplication;
idle queue and concurrency metadata can be reclaimed safely under churn.
- Added PostgreSQL migrations V7–V11 for execution revisions, maintenance and
retention indexes, and sharded queue monitoring counters. Historical V1–V6
migrations remain unchanged. Monitoring and queue discovery use bounded or
indexed operations instead of repeatedly scanning all retained jobs.
- Fixed Redis claim locks surviving timed-out/interrupted acquisitions, partial
bulk acquisition and failed workflow preparation. Token-checked cleanup
preserves replacement ownership and caller interruption, and tries remaining
locks after an ordinary cleanup failure. Expiry remains the crash fallback.
- Raised the Redis data-node minimum to 7.4 and added an offline format-2 index
migration for existing namespaces. Script-cache recovery uses key-routed
EVALSHA/EVAL, including after Cluster promotion and live slot migration.
Standalone, Sentinel and Cluster contracts use real Redis; failover does not
imply zero acknowledged-write loss.
- Hardened application-classloader handling, dashboard authentication/error
responses, Redis search capability enforcement, and optional asynchronous
metrics refresh. Updated locked dependencies, including the patched Vitest
development toolchain.
- Expanded regression, migration, topology and production validation. The soak
harness records live invariants, operation latency, retention and fault
evidence, and recovers uncertain producer acknowledgements across datastore
pauses/restarts without changing job IDs.
- Updated installation and migration documentation for 1.0.0. Commercial
support starts with 1.0 through [hemju.com](https://hemju.com/), with inquiries
to [sales@hemju.com](mailto:sales@hemju.com). LingoHub is a reference customer.

## 0.3.0

Expand Down
35 changes: 28 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,24 @@ Threadmill is published to Maven Central under the `com.hemju.threadmill`
group. Pick the core plus the store you run against (and the Spring Boot
starter if you use Spring):

These examples target **1.0.0**, which is being prepared for release on this
branch. Until publication completes, the latest published release remains
**0.3.0**. Upgrade existing installations using the
[0.3.0-to-1.0 migration guide](docs/compatibility.md#upgrade-from-v030).

```kotlin
// build.gradle.kts
implementation("com.hemju.threadmill:threadmill-core:0.3.0")
implementation("com.hemju.threadmill:threadmill-store-postgres:0.3.0") // or -store-redis / -store-memory
implementation("com.hemju.threadmill:threadmill-spring-boot:0.3.0") // optional Spring Boot integration
implementation("com.hemju.threadmill:threadmill-core:1.0.0")
implementation("com.hemju.threadmill:threadmill-store-postgres:1.0.0") // or -store-redis / -store-memory
implementation("com.hemju.threadmill:threadmill-spring-boot:1.0.0") // optional Spring Boot integration
```

```xml
<!-- Maven -->
<dependency>
<groupId>com.hemju.threadmill</groupId>
<artifactId>threadmill-core</artifactId>
<version>0.3.0</version>
<version>1.0.0</version>
</dependency>
```

Expand Down Expand Up @@ -124,15 +129,15 @@ See [docs/quickstart.md](docs/quickstart.md) for a complete Spring walkthrough,

## Storage backends

**PostgreSQL** is the primary production backend. Indexed scalar columns
**PostgreSQL 18+** is the primary production backend. Indexed scalar columns
denormalize the indexed job state; the body column holds the
JSON-serialized job. Per-state counts come from a counter table maintained
by a trigger (so the observability path never contends with the claim
path). Migrations are applied automatically on startup; an
`emitPendingSql()` method produces pending SQL for teams that prefer
Flyway/Liquibase, and `emitCleanInstallSql()` emits the full clean-install DDL.

**Redis** is a fully supported first-class backend. Every multi-key state
**Redis 7.4+** is a first-class backend and requires `noeviction`. Every multi-key state
transition is a single atomic Lua script. Standalone, Sentinel, and Cluster
topologies are configured through one factory path. Redis Cluster uses a
single `{threadmill}` hash slot for correctness; it is topology/failover
Expand Down Expand Up @@ -220,7 +225,9 @@ stable API.

## Status

Shipped in v1:
The **1.0.0 release candidate** includes the features below. Publication is
pending the [release checks](docs/release-checklist.md), including completed
PostgreSQL and Redis soak qualification.

- Job model with append-only state history, optimistic-lock versioning,
relationship and result fields, and bounded size.
Expand Down Expand Up @@ -317,6 +324,20 @@ Java is formatted with **Palantir Java Format** in its `GOOGLE` style
everyone on the team ends up with byte-identical output, and `./gradlew
check` fails on violations.

## Reference customer

<a href="https://lingohub.com/">
<img src="docs/assets/lingohub-logo.png" alt="LingoHub" width="320">
</a>

[LingoHub](https://lingohub.com/) uses Threadmill for background job processing.

## Commercial support

Starting with Threadmill **1.0**, commercial support will be available through
[hemju.com](https://hemju.com/). Contact [sales@hemju.com](mailto:sales@hemju.com)
to discuss commercial support for your team.

## License

Apache License 2.0. See [LICENSE](LICENSE).
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ package com.hemju.threadmill.gradle

/** The single source of truth for the version of every published Threadmill module. */
object ThreadmillVersion {
const val CURRENT = "0.3.0"
const val CURRENT = "1.0.0"
}
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ services:
timeout: 5s
retries: 10
redis:
image: redis:7-alpine
image: redis:7.4-alpine
container_name: threadmill-local-redis
command: ["redis-server", "--appendonly", "yes", "--appendfsync", "everysec"]
ports:
Expand Down
Loading
Loading