Skip to content

Releases: riverqueue/river

java/v0.1.0-alpha.1

java/v0.1.0-alpha.1 Pre-release
Pre-release

Choose a tag to compare

@brandur brandur released this 25 Oct 05:36
972133b

Added

  • First alpha release of River for Java. com.riverqueue:river provides a Java 21 client and worker runtime for Postgres and SQLite, sharing River's database schema and job protocol with other River clients. Includes typed jobs, virtual-thread workers, transactional inserts, unique, scheduled, periodic, and resumable jobs, queue management, hooks, middleware, events, and migrations.
  • Standalone migration CLI, published as com.riverqueue:river-cli with classifier all. The executable JAR bundles both JDBC drivers and supports applying, reverting, listing, and exporting migrations.

Changed

  • Breaking: Replaced Extension and Client.withExtension with operation-specific hook and middleware interfaces, matching River for Go: HookInsertBegin, HookPeriodicJobsStart, HookWorkBegin, HookWorkEnd, JobInsertMiddleware, and WorkerMiddleware. Register them with Client.withPlugins to activate all interfaces an instance implements, or with withHooks and withMiddleware to activate only that side. Plugins run before separately registered hooks and middleware. Each operation-specific interface supports lambdas and checked exceptions. The afterClaim callback was removed; use worker middleware for work before the handler. PR #1519.
  • HookWorkEnd.workEnd returns the attempt's failure: return the incoming failure to preserve it, a replacement to change it, or null to report success. Each hook receives the previous hook's result, so hooks migrated from Extension.afterWork must explicitly return the failure to preserve the outcome. A work-begin failure now skips both the handler and work-end hooks. PR #1519.
  • Insertion middleware now receives the batch's submissions and returns its insertion results. It runs once per batch, including a one-element batch for single inserts, while insertion hooks run once per job. Hooks and middleware run inside the insertion's transaction. PR #1519.
  • Renamed the internal River Pro storage interface and its registration method from Plugin/Client.withPlugin to Pilot/Client.withPilot. Plugin is now the common marker interface for application hooks and middleware. PR #1519.

Fixed

  • Fail retries that omit a saved cursor-only step, while preserving their checkpoints for recovery. Jobs with an existing step marker retain their behavior, including legacy stale cursor entries. PR #1534.

  • Replace whole top-level metadata values on SQLite, including JSON null, matching Postgres. Completed resumable cursor steps no longer run again because of stale cursor entries. PR #1533.

ruby/v0.13.0

Choose a tag to compare

@brandur brandur released this 08 Oct 05:53
8a8828b

Added

  • Expanded checks against Go-generated conformance fixtures to cover cron schedules, snooze counters, all shared metadata keys, and queue-control and leadership notification emission through both SQL drivers. PR #1463.
  • Added CRON_TZ= / TZ= prefixes, ? wildcards, and Go-style @every durations to PeriodicCron. PR #1463.

Changed

  • Ruby is now developed in the main River repository. Gem names and require paths are unchanged. The four public gems (riverqueue, riverqueue-activerecord, riverqueue-sequel, and riverqueue-rails) are released together. PR #1463.
  • Job insertion accepts only available, pending, and scheduled states. running, retryable, and terminal states are rejected, including when set by insertion hooks. PR #1463.
  • Insertion rejects queue names containing characters other than ASCII letters, digits, underscores, hyphens, colons, or periods. Nonzero UniqueOpts#by_period values must be at least one second. PR #1463.
  • Attempt counts must be between 0 and 32,767, and max_attempts must be between 1 and 32,767. These bounds also apply on SQLite. job_retry raises ArgumentError when another attempt would exceed the limit. PR #1463.
  • Job and queue metadata updates require a Hash; JSON-encoded strings are no longer accepted. Pass an empty Hash to clear metadata. Updates to attempted_by require string entries. PR #1463.

Fixed

  • Externally claimed jobs honor worker retry hooks, including Active Job's retry policy, and use their claim timestamps for queue-wait statistics and attempt errors. Worker initialization failures are logged without losing the reported work error. PR #1463.
  • Externally reported cancellation, snooze, and interruption signals follow normal worker finalization instead of being recorded as ordinary failures. PR #1463.
  • Graceful shutdown and queue removal continue observing remote cancellation until active jobs finish, including when worker timeouts are disabled or cancellation polling temporarily fails. Draining waits respect polling intervals and wake when the last attempt finishes. PR #1463.
  • Blocking lifecycle calls from the client's own runtime threads fail immediately instead of waiting for themselves. Workers can still request nonblocking stop. PR #1463.
  • Inserts reject states other than available, pending, and scheduled, preventing stranded running jobs without attempt timestamps. Insertion hooks are checked after all callbacks run, and invalid batches roll back hook writes. PR #1463.
  • The Sequel driver preserves application ArgumentError exceptions raised inside SQLite transactions after rolling back, including validation errors from insertion hooks. PR #1463.
  • Job inserts and updates reject attempt limits above 32,767 consistently across databases. Attempt updates and manual retries enforce the portable counter range, and claims tolerate counters already at the limit. PR #1463.
  • Wildcard queue pause and resume publish events for every affected queue, including beyond 100 queues. Events use the update's snapshots so concurrent changes cannot replace their contents or introduce unrelated queues. PR #1463.
  • Manually retrying a cancelled job clears the old cancellation request so its next attempt can complete. PR #1463.
  • Cancellation wins atomically over discarded attempts and other state transitions, including the last attempt or a worker that disables retries. PR #1463.
  • Stuck-job rescue falls back to the default retry policy per job when an application policy fails or returns an invalid time, allowing recovery and cleanup to continue. PR #1463.
  • Clients without consumer queues run configured periodic jobs and maintenance services. Adding periodic jobs starts maintenance when needed; registration is rejected when leader election is disabled. PR #1463.
  • Bulk deletion excludes running jobs before applying its limit and locks eligible PostgreSQL rows so concurrent retries cannot cause deletion of jobs that no longer match the filters. PR #1463.
  • Stuck-job rescue respects longer or disabled client and worker timeouts and scans past protected attempts without consuming the rescue limit. PR #1463.
  • Snoozing and rescue tolerate JSON counters that overflow SQLite's floating-point representation. PR #1463.
  • Snoozing tolerates nonnumeric metadata counters instead of failing on booleans or collections. Ruby-specific recovery cases are tested independently of the shared Go fixtures. PR #1463.
  • Periodic jobs now carry periodic: true and, when named, river:periodic_job_id, while preserving constructor options and application metadata. PR #1463.
  • Queue pause, resume, metadata changes, and leader resignation now broadcast notifications for clients in other languages. Notifications commit and roll back with the corresponding database change. PR #1463.
  • Cron schedules include both occurrences of repeated daylight-saving times when given a local reference time. PR #1463.
  • Job insertion rejects invalid queue names, nonpositive attempt limits, and nonzero uniqueness periods shorter than one second before writing any jobs. PR #1463.
  • Custom maintenance service failures are logged without blocking other services, stuck-job rescue, or cleanup. PR #1463.
  • Job updates reject non-object metadata before writing to the database, preserving the existing job when invalid metadata is supplied. PR #1463.
  • Job completion atomically checks for cancellation so a concurrent cancellation cannot be overwritten by completion, including when finalization hooks are configured. PR #1463.
  • Job updates reject nonpositive attempt limits consistently on PostgreSQL and SQLite. PR #1463.
  • Job lifecycle events report worker execution and completion durations separately using a monotonic clock. PR #1463.
  • Finalization hooks that delete jobs honor concurrent cancellation and record the cancelled attempt instead of deleting it. PR #1463.
  • Queue metadata writes and job metadata merges reject non-object values before changing persisted data. Job updates also reject non-string worker IDs in attempt histories. PR #1463.
  • Unique inserts with exclude_kind preserve the existing job's kind when a different kind conflicts, on both databases and through both drivers. PR #1463.
  • Retrying, snoozing, and interrupting jobs preserve the original queue-wait duration in lifecycle events. PR #1463.
  • Invalid rescue counters no longer prevent other stuck jobs in the batch from being rescued. PR #1463.
  • SQLite retains only the newest 100 worker IDs when claiming a job, matching PostgreSQL. PR #1463.
  • Attempt-error serialization supports frozen timestamps and preserves the caller's timezone. PR #1463.

rust/v0.3.0

Choose a tag to compare

@brandur brandur released this 07 Oct 17:25
44cee8b

Changed

  • Breaking: Renamed WorkerRegistry to Workers, register to add, and register_fn to add_fn to align worker registration with Go's naming. Both methods retain their Result return type and registration behavior. PR #1469.

rust/v0.2.0

Choose a tag to compare

@brandur brandur released this 07 Oct 05:09
31aeb77

Changed

  • Breaking: Renamed Client::start_with_graceful_shutdown to Client::start_with_graceful_stop to match River's standard start/stop terminology. This is a rare breaking name change as the new Rust API stabilizes. PR #1467.

rust/v0.1.0

Choose a tag to compare

@brandur brandur released this 07 Oct 04:34
53e6e2a

Added

  • First release of River for Rust. riverqueue provides a typed, Tokio-based client for PostgreSQL (through SQLx) and SQLite that shares River's database schema and job protocol with River for Go, so Rust and Go clients can insert and work jobs in the same database. It includes typed workers, transactional inserts and completion, unique, scheduled, periodic, and resumable jobs, queue management, job cancellation, events, hooks, middleware, leader election, and maintenance services. Companion crates provide #[derive(JobArgs)] with unique options (riverqueue-macros), migration application and validation using Go's migration history (riverqueue-migrate), the riverqueue command for migrations and benchmarks (riverqueue-cli), and fixtures, insertion assertions, and worker test helpers (riverqueue-test). Requests run with .tx(...) use the caller's transaction directly, without a savepoint or nested transaction, like River for Go's *Tx methods. Errors may leave partial writes, so roll back the transaction or create an explicit savepoint around the request. PR #1442.

rust/v0.1.0-alpha.1

rust/v0.1.0-alpha.1 Pre-release
Pre-release

Choose a tag to compare

@brandur brandur released this 07 Oct 03:58
1e4d6f8

Added

  • First preview release of River for Rust. riverqueue provides a typed, Tokio-based client for PostgreSQL (through SQLx) and SQLite that shares River's database schema and job protocol with River for Go, so Rust and Go clients can insert and work jobs in the same database. It includes typed workers, transactional inserts and completion, unique, scheduled, periodic, and resumable jobs, queue management, job cancellation, events, hooks, middleware, leader election, and maintenance services. Companion crates provide #[derive(JobArgs)] with unique options (riverqueue-macros), migration application and validation using Go's migration history (riverqueue-migrate), the riverqueue command for migrations and benchmarks (riverqueue-cli), and fixtures, insertion assertions, and worker test helpers (riverqueue-test). Requests run with .tx(...) use the caller's transaction directly, without a savepoint or nested transaction, like River for Go's *Tx methods. Errors may leave partial writes, so roll back the transaction or create an explicit savepoint around the request. PR #1442.

v0.49.0

Choose a tag to compare

@brandur brandur released this 05 Oct 15:50
da5a26e

⚠️ If using River Pro, make sure to upgrade it to at least River Pro v0.32.0 to get a compatible package.

Changed

  • Internal transaction helpers now reuse existing transactions instead of creating unnecessary savepoints. Callers of transactional APIs must roll back their transaction on error; applications that need partial rollback can create an explicit savepoint around the River call. Set RIVER_USE_LEGACY_SUBTRANSACTIONS=1 (or true) before starting the application to restore savepoints in these helpers, including those used by River Pro. This is a temporary compatibility fallback planned for removal in a future release. PR #1420.

Fixed

  • Fixed rivertest.Worker to honor a configured Config.JobStuckThreshold for stuck job detection. Previously, it always used an internal 5 second threshold, so the Job appears to be stuck log line was emitted at a different time than it would be under a real client. PR #1418.
  • Fixed SQLite job cleanup stalling when excluded queues fill the oldest batch, and added support for included queue filters so per-queue retention works on SQLite. PR #1417.
  • Fixed a unique insert skipped as a duplicate of a job of a different kind (possible with UniqueOpts.ExcludeKind) changing the existing job's kind to its own. PR #1421.

v0.48.0

Choose a tag to compare

@brandur brandur released this 01 Oct 04:20
c2c3804

⚠️ This release contains a new database migration, version 8, but it only affects SQLite:

  • If you're on Postgres, you can ignore it with no adverse effect.
  • If you're on SQLite, it rebuilds river_job to add an AUTOINCREMENT keyword to the primary key, preventing a possible edge case where generated job IDs could be reused after deletion. It's not necessary to run the migration for River to work, but it's a good idea to get it in when convenient. PR #1390.

Added

  • Added support for YugabyteDB. When LISTEN/NOTIFY is unavailable or disabled, clients automatically poll for running job cancellations and queue pause, resume, and metadata changes, and skip unsupported notification broadcasts. This works with the default PollOnly: false. Native notifications require YugabyteDB 2025.2.3 or later with ysql_yb_enable_listen_notify=true on both Masters and TServers. PR #1347.
  • Added Config.LeaderElectionDisabled to let a client work jobs without participating in leader election or running maintenance services. Other eligible clients in the same database and schema continue handling scheduling, retries, periodic enqueueing, rescue, and cleanup. PR #1382.
  • Added Config.FetchOnlyKnownKinds to restrict job fetching to registered worker kinds, including aliases. Clients with different workers can share a queue while leaving unknown jobs available without consuming attempts. Disabled by default; leader election and stuck-job rescue behavior are unchanged. PR #1396.

Changed

  • UniqueOpts.ByPeriod now derives a job's period from its effective scheduled time (InsertOpts.ScheduledAt when set, otherwise the insertion time), so scheduled jobs are deduplicated against other jobs scheduled in the same period rather than against jobs inserted in the same period. Periods are also now always measured in UTC, so processes and ScheduledAt values in different time zones produce the same unique key for the same period. Unique keys for scheduled ByPeriod jobs, and for any ByPeriod job inserted from a process whose local time zone isn't UTC, differ from those produced by previous versions. During a rolling upgrade, old and new clients may therefore each insert one job for such a period; jobs that aren't scheduled and are inserted from UTC processes are unaffected. PR #1377.
  • UniqueOpts{ExcludeKind: true} alone is now rejected at insert time instead of being silently ignored. UniqueOpts.isEmpty() now considers ExcludeKind, in line with the equivalent handling in internal/dbunique. Warning: This new rejection can be considered a minor breaking change. PR #1404.

Fixed

  • Fixed cancelled transaction starts leaving Turso connections unusable, which could prevent maintenance from recovering after a startup failure. PR #1347.
  • Fixed maintenance startup failures leaving a client renewing leadership with maintenance stopped in poll-only mode. After exhausting startup retries, clients now request local resignation without depending on database notifications. PR #1347.
  • Fixed JobRescuer overwriting jobs that complete, leave the running state, or are claimed again by another worker after being fetched for rescue, preserving their state, errors, metadata, and timestamps across PostgreSQL and SQLite drivers. Fixes #1302. PR #1373.
  • Fixed SQLite job list pagination skipping or repeating jobs by formatting cursor timestamps consistently with stored timestamps. PR #1374.
  • Improved PostgreSQL job listing performance when filtering by one finalized state (completed, cancelled, or discarded) and sorting by finalized time, including in River UI. PR #1374.
  • Fixed rivermigrate leaving river_migration rows behind after migrating a non-main migration line down through its version 1, which caused a later up migration of that line to skip version 1. With MigrateTx, rows for every removed version were left behind. PR #1378.
  • Fixed river bench inserting every benchmark job with a num arg of 0 instead of numbering jobs sequentially. PR #1379.
  • Attempt errors that are valid JSON but don't have the shape River writes (for example an at timestamp in another format, an attempt stored as a string, or an error or trace that isn't a string) are now decoded on a best effort basis when reading jobs from the database. Timestamps outside RFC 3339 are left zero; stored values are unchanged. Previously a single such element made its job unreadable, and if that happened while fetching jobs, every job locked in the same fetch was left running indefinitely. PR #1380.
  • A fetched job whose row can't be decoded (for example a SQLite job whose tags were changed to something other than an array of strings) no longer leaves every job locked in the same fetch stuck running. The other jobs are worked normally, while the undecodable job's attempt fails with an error describing the decode failure, and it's retried or discarded like any other failed job. Its original values are preserved, with non-array errors values wrapped to append the failure or rescue error. Neither the rescuer nor the SQLite scheduler fails on such a job, so its retry doesn't stop other jobs from being rescued or scheduled. PR #1380.
  • Fixed SQLite notification listeners delivering notifications from before a subscription or from an unsubscribe gap. Notification reads now fetch subscribed topics in bounded batches, and cleanup deletes expired notifications in batches of 10,000 rows (reduced to 1,000 after repeated timeouts), with pauses between batches to reduce write lock contention. PR #1381.
  • Fixed the job completer panicking when a job it was finalizing had its state changed concurrently, like being moved to pending out of band, or being rescued while the completer's update waited on the row lock (in which case PostgreSQL returns the job's pre-update running row). Such jobs are now skipped without emitting a completion event. PR #1383.
  • Fixed JobList pagination skipping or repeating jobs when ordering by JobListOrderByTime with multiple states. Cursors now use the same time field as the list's ordering (the one for the first listed state) rather than the one for each job's own state. Jobs where that field is null, like finalized_at for unfinalized jobs, are paginated correctly and consistently sort last in ascending order and first in descending order on all drivers. Ordering by JobListOrderByTime with an empty States() filter now uses scheduled_at instead of returning an error. PR #1384.
  • A SQLite job whose args, attempted_by, errors, metadata, or tags were changed to text that isn't valid JSON no longer makes every fetch from its queue fail with a "malformed JSON" error. The job's attempt fails like that of any other job that can't be decoded, and completing, rescuing, or scheduling it no longer fails either. Invalid values are left in place, except that an invalid errors value is kept as a string in a new array so that attempt errors can still be appended. PR #1386.
  • Fixed UniqueOpts.ByArgs skipping distinct jobs or failing inserts when JSON keys contain path syntax (like user.id), are empty, or come from unnamed tags like json:",omitempty". Unaffected unique keys remain unchanged; affected jobs may be inserted again after upgrading or by old and new clients during a rolling upgrade. PR #1387.
  • Fixed JobListCursor.UnmarshalText rejecting valid cursors whose URL-safe base64 encoding contains - or _, so cursors produced by MarshalText always round-trip. PR #1388.
  • Fixed SQLite drivers deleting jobs in a finalized state whose retention period was set to -1 (keep forever), like Config.DiscardedJobRetentionPeriod: -1, whenever another state's retention period was finite. PR #1389.
  • Fixed SQLite reusing the ID of a deleted job when that job held the largest ID, which could cause an ID observed earlier to refer to an unrelated job later. PR #1390.
  • Fixed the Job appears to be stuck log line reporting the client-level JobTimeout instead of the worker-level timeout when a worker overrides Timeout. PR #1394.
  • Fixed job cancellations received during a fetch being lost before the fetched jobs started. Matching jobs now receive cancellation before work begins. PR #1397.
  • Fixed SQLite JobCancel and JobCancelTx notifying running workers through the shared control outbox, so their contexts are cancelled when the transaction commits. PR #1398.
  • Fixed SQLite InsertMany and InsertManyTx reporting multiple inserted jobs when a batch contains the same active unique key more than once. Such batches now fail atomically, matching PostgreS...
Read more

v0.47.0

Choose a tag to compare

@brandur brandur released this 31 Aug 22:21
48c0036

Added

  • Added hook for River's CLI command framework that can be used to inject custom SQLite initialization. Used by River Pro. PR #1369.

v0.46.0

Choose a tag to compare

@brandur brandur released this 29 Aug 13:47
195adeb

Added

  • Added riverdatabasesql.NewWithPgxListener for using a dedicated Pgx pool to receive Postgres notifications while continuing to execute jobs and transactions through database/sql. PR #1366.