Skip to content
Merged
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
75 changes: 71 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ All notable changes to taskito are documented here. The format is based on
[Semantic Versioning](https://semver.org/spec/v2.0.0.html). All SDKs (Python, Node, Java) and the
underlying Rust crates are released together, in lock-step.

## Unreleased
## 0.22.0

Attach and executor release. `taskito-server` becomes a standalone binary that schedules for an
app written against any SDK, workers attach to it over a shared worker protocol, and an executor
side-channel carries progress, logs and middleware toggles back. Namespace scoping is completed
across the id-addressed storage APIs, and cross-SDK parity lands the operator admin surface,
predicates, retry policy and the full event taxonomy in Node and Java.

### Changed

Expand All @@ -28,9 +34,24 @@ underlying Rust crates are released together, in lock-step.
`status` filter (`DeliveryStatus`, Python). Same non-breaking contract — a string still works.
Internally, Python's workflow tracker and saga orchestrator now compare node/run status against
the enums rather than string literals.
- **The Node native binding is built on napi 3.** An internal upgrade from napi 2 with no change
to the published API; the minimum supported Node version stays 20.

### Added

- **`taskito-server`: a standalone scheduler, attach listener and dashboard.** One Rust binary
runs the scheduler loop and serves the dashboard for an app written against any SDK, so the fat
application image no longer has to be redeployed as a worker. Workers attach to it over a shared
worker protocol — a JSON header line plus raw payload bytes, one format for both the prefork
pipe and the attach socket — dispatched through a `RemoteDispatcher` in the core. Database
credentials stay in the server rather than the app image.
- **A `taskito executor` subcommand in every SDK.** Python, Node and Java each ship an executor
that attaches to a running server, advertises the tasks it can run, and executes only those —
the process side of the attach topology above.
- **An executor side-channel for progress, logs and middleware toggles.** An attached executor
reports `update_progress`, task logs, and middleware enable/disable back over the same
connection, so an attached worker is observable and controllable exactly like an in-process one.
Implemented in the core and wired through all three SDKs.
- **A Helm chart for `taskito-server`, with annotation-driven sidecar injection.**
`deploy/helm/taskito-server` installs the scheduler, dashboard and injector as one release, and
refuses to render the combinations the server rejects at boot — an attach listener with no token,
Expand Down Expand Up @@ -96,16 +117,36 @@ underlying Rust crates are released together, in lock-step.
`durationMs` on Node's, and `duration_ms` on Python's job event payloads. Java also gains
`NodeSnapshot.durationMs()` / `compensationDurationMs()` and `TaskContext.elapsedMs()`.
- **Full lifecycle event taxonomy in the Node and Java SDKs.** Both grow from the 4 job-outcome
events to the 26-event cross-SDK taxonomy: enqueue, per-attempt failure (`job.failed`), worker
events to the 29-event cross-SDK taxonomy: enqueue, per-attempt failure (`job.failed`), worker
lifecycle, queue pause/resume, workflow submit/terminal/gate, saga compensation, and predicate
rejection. Node types `queue.on()` per event name (`EventMap`); Java adds queue-level
`taskito.onEvent(EventName, Consumer<TaskitoEvent>)` with typed event records alongside the
unchanged worker-scoped `Worker.Builder.on(...)`. Webhooks can subscribe to every event name.
`predicate.cancelled` stays Python-only, and Python now emits the previously dormant
`workflow.submitted`.
`predicate.cancelled` is reserved in all three — subscribable, typed and webhook-matchable —
but only Python emits it: Node and Java gate at enqueue, where a terminal skip is
`predicate.skipped` because no job exists yet.
- **Java webhook deliveries use the dotted wire names.** Bodies that previously said
`"event": "success"` now say `"event": "job.completed"`; stored subscriptions with the legacy
four names keep matching, and job-outcome bodies gain `duration_ms`.
- **Retention dry-run.** `dry_run_retention()` reports what a purge would delete right now — per
table and in total, against the windows the elected cleaner actually published — so a window can
be sized before it deletes anything. The dashboard echoes those live windows rather than
guessing at the defaults.
- **Retry predicates in the Node and Java SDKs.** A per-task `retryOn` classifies a thrown error:
return `false` and the job dead-letters immediately, whatever retry budget is left. One
predicate covers both directions — test for the errors worth retrying, or negate a test for the
permanent ones. Java can signal the same intent by exception type instead, via
`RetryableException` / `NonRetryableException`, so a domain hierarchy doubles as the policy.
- **Push-dispatch in the Node and Java shells.** Both accept the `pushDispatch` option, so an
enqueue wakes the scheduler immediately instead of waiting out the poll interval.
- **Node standalone health and readiness helpers.** Liveness and readiness checks that run without
the dashboard, for a worker deployment that serves no HTTP of its own.
- **Python periodic-schedule catalog management.** `list_periodic()`, `delete_periodic(name)`,
`pause_periodic(name)` and `resume_periodic(name)` operate the cron catalog at runtime, so a
schedule can be suspended or dropped without a redeploy.
- **Java classifier jars and GraalVM metadata.** Per-platform classifier artifacts publish
alongside the fat jar, and bundled reachability metadata makes the SDK usable from a
GraalVM native image.

### Fixed

Expand All @@ -132,6 +173,32 @@ underlying Rust crates are released together, in lock-step.
default `atexit` teardown. Taskito now initializes OpenSSL itself with that teardown
suppressed (`OPENSSL_INIT_NO_ATEXIT`) before the first Postgres connection.

- **Namespace scoping is complete across the id-addressed storage APIs.** Every method that
addresses a row by id now takes the namespace it must match, so one tenant can no longer read,
mutate or complete another's job by guessing its id. Cross-namespace dependencies are rejected
at enqueue, and workflow runs are scoped through the store handle.
- **Dashboard settings are compare-and-set in every SDK.** Two dashboards editing the same JSON
settings document no longer silently overwrite each other; a stale write is rejected instead of
clobbering the newer one.
- **The predicate event taxonomy is complete across SDKs.** The gate outcome names each SDK
recognized diverged, so a webhook subscribed to one could not be registered against another.
All three now carry the same set. Separately, `workflow.submitted` fires on run submission
rather than staying dormant.
- **Java null-safety contract and native platform coverage.** Nullability is declared across the
public API, and the published artifact carries every supported platform's native library rather
than only the build host's. `JobRec` reads are guarded by the backend monitor, and the build no
longer silently disables every compile task.
- **The Redis task-metric retention purge is bounded.** It read its whole below-cutoff window in
one `ZRANGEBYSCORE` and issued a `GET` per id, so the first sweep after a long retention gap
stalled the maintenance tick. It now drains in batches like every other purge, with one `MGET`
per batch.
- **Batch enqueue applies `unique_key` dedup.** A batch carrying any keyed entry now routes
through the unique path rather than the plain multi-row insert, which would hit the partial
unique index and fail the whole batch the moment a key duplicated an active job. Shared by the
Node and Java batch enqueues; a keyless batch keeps the chunked throughput path, and Python
still batch-inserts without dedup.
- Version drift in the Node CLI banner and the Java installation docs.

## 0.21.0

Overload-controls and retention release. The queue gains admission and load-shedding controls,
Expand Down
16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ resolver = "2"
# release bumps `version` in exactly one place. `rust-version` is verified by
# the CI msrv job; sea-query 1.x pins the floor at 1.88.
[workspace.package]
version = "0.21.0"
version = "0.22.0"
edition = "2021"
license = "MIT"
repository = "https://github.com/ByteVeda/taskito"
Expand Down
4 changes: 2 additions & 2 deletions deploy/helm/taskito-server/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ type: application
# Chart version and appVersion move together: the chart only ever ships the
# image built from the same commit, and `node scripts/version.mjs --check`
# enforces that in CI.
version: 0.21.0
appVersion: "0.21.0"
version: 0.22.0
appVersion: "0.22.0"
home: https://taskito.grigori.in
sources:
- https://github.com/ByteVeda/taskito
Expand Down
2 changes: 1 addition & 1 deletion docs/app/lib/version.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
// AUTO-GENERATED from /CHANGELOG.md by scripts/sync-changelog.mjs — do not edit directly.
export const VERSION = "0.21.0";
export const VERSION = "0.22.0";
2 changes: 1 addition & 1 deletion docs/content/docs/java/api-reference/testing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ would against a native backend. For a walkthrough with assertions, see the
[Testing guide](/java/guides/operations/testing).

```kotlin
testImplementation("org.byteveda:taskito-test:0.21.0")
testImplementation("org.byteveda:taskito-test:0.22.0")
```

## `InMemoryTaskito`
Expand Down
14 changes: 7 additions & 7 deletions docs/content/docs/java/getting-started/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ per-platform classifier artifact next to a native-free main jar — add both.
plugins { id("com.google.osdetector") version "1.7.3" }

dependencies {
implementation("org.byteveda:taskito:0.21.0")
runtimeOnly("org.byteveda:taskito:0.21.0:${osdetector.classifier}") // native library
annotationProcessor("org.byteveda:taskito-processor:0.21.0") // compile-time TaskHandler bindings
implementation("org.byteveda:taskito:0.22.0")
runtimeOnly("org.byteveda:taskito:0.22.0:${osdetector.classifier}") // native library
annotationProcessor("org.byteveda:taskito-processor:0.22.0") // compile-time TaskHandler bindings
}
```

The [osdetector](https://github.com/google/osdetector-gradle-plugin) plugin
resolves your platform's classifier; you can also write it literally, e.g.
`runtimeOnly("org.byteveda:taskito:0.21.0:linux-x86_64")`.
`runtimeOnly("org.byteveda:taskito:0.22.0:linux-x86_64")`.

</Tab>
<Tab value="Maven">
Expand All @@ -42,12 +42,12 @@ resolves your platform's classifier; you can also write it literally, e.g.
<dependency>
<groupId>org.byteveda</groupId>
<artifactId>taskito</artifactId>
<version>0.21.0</version>
<version>0.22.0</version>
</dependency>
<dependency>
<groupId>org.byteveda</groupId>
<artifactId>taskito</artifactId>
<version>0.21.0</version>
<version>0.22.0</version>
<classifier>${os.detected.classifier}</classifier>
<scope>runtime</scope>
</dependency>
Expand All @@ -63,7 +63,7 @@ resolves your platform's classifier; you can also write it literally, e.g.
<path>
<groupId>org.byteveda</groupId>
<artifactId>taskito-processor</artifactId>
<version>0.21.0</version>
<version>0.22.0</version>
</path>
</annotationProcessorPaths>
</configuration>
Expand Down
2 changes: 1 addition & 1 deletion docs/content/docs/java/guides/integrations/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ add the matching runtime dependency yourself.

```kotlin
// Only add what you use.
implementation("org.byteveda:taskito-spring:0.21.0") // Spring Boot 3 starter
implementation("org.byteveda:taskito-spring:0.22.0") // Spring Boot 3 starter
implementation("io.micrometer:micrometer-observation:1.13.6") // contrib.TaskitoObservation
implementation("io.sentry:sentry:7.14.0") // contrib.SentryMiddleware
```
Expand Down
2 changes: 1 addition & 1 deletion docs/content/docs/java/guides/integrations/spring.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ The `taskito-spring` starter auto-configures a `Taskito` bean for Spring Boot 3
applications.

```kotlin
implementation("org.byteveda:taskito-spring:0.21.0")
implementation("org.byteveda:taskito-spring:0.22.0")
```

When the starter is on the classpath, `TaskitoAutoConfiguration` builds and
Expand Down
2 changes: 1 addition & 1 deletion docs/content/docs/java/guides/resources/testing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ backend — no JNI, no disk. The resource runtime is backend-independent, so
resources build, scope, and tear down exactly the same way over it:

```kotlin
testImplementation("org.byteveda:taskito-test:0.21.0")
testImplementation("org.byteveda:taskito-test:0.22.0")
```

```java
Expand Down
Loading
Loading