Repository navigation
Releases: riverqueue/river
Releases · riverqueue/river
Release list
java/v0.1.0-alpha.1
Added
- First alpha release of River for Java.
com.riverqueue:riverprovides 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-cliwith classifierall. The executable JAR bundles both JDBC drivers and supports applying, reverting, listing, and exporting migrations.
Changed
- Breaking: Replaced
ExtensionandClient.withExtensionwith operation-specific hook and middleware interfaces, matching River for Go:HookInsertBegin,HookPeriodicJobsStart,HookWorkBegin,HookWorkEnd,JobInsertMiddleware, andWorkerMiddleware. Register them withClient.withPluginsto activate all interfaces an instance implements, or withwithHooksandwithMiddlewareto activate only that side. Plugins run before separately registered hooks and middleware. Each operation-specific interface supports lambdas and checked exceptions. TheafterClaimcallback was removed; use worker middleware for work before the handler. PR #1519. HookWorkEnd.workEndreturns the attempt's failure: return the incoming failure to preserve it, a replacement to change it, ornullto report success. Each hook receives the previous hook's result, so hooks migrated fromExtension.afterWorkmust 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.withPlugintoPilot/Client.withPilot.Pluginis 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
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@everydurations toPeriodicCron. 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, andriverqueue-rails) are released together. PR #1463. - Job insertion accepts only
available,pending, andscheduledstates.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_periodvalues must be at least one second. PR #1463. - Attempt counts must be between 0 and 32,767, and
max_attemptsmust be between 1 and 32,767. These bounds also apply on SQLite.job_retryraisesArgumentErrorwhen 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_byrequire 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, andscheduled, preventing strandedrunningjobs 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
ArgumentErrorexceptions 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: trueand, 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_kindpreserve 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
Changed
- Breaking: Renamed
WorkerRegistrytoWorkers,registertoadd, andregister_fntoadd_fnto align worker registration with Go's naming. Both methods retain theirResultreturn type and registration behavior. PR #1469.
rust/v0.2.0
Changed
- Breaking: Renamed
Client::start_with_graceful_shutdowntoClient::start_with_graceful_stopto 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
Added
- First release of River for Rust.
riverqueueprovides 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), theriverqueuecommand 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*Txmethods. 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
Added
- First preview release of River for Rust.
riverqueueprovides 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), theriverqueuecommand 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*Txmethods. Errors may leave partial writes, so roll back the transaction or create an explicit savepoint around the request. PR #1442.
v0.49.0
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(ortrue) 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.Workerto honor a configuredConfig.JobStuckThresholdfor stuck job detection. Previously, it always used an internal 5 second threshold, so theJob appears to be stucklog 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
- If you're on Postgres, you can ignore it with no adverse effect.
- If you're on SQLite, it rebuilds
river_jobto add anAUTOINCREMENTkeyword 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/NOTIFYis 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 defaultPollOnly: false. Native notifications require YugabyteDB 2025.2.3 or later withysql_yb_enable_listen_notify=trueon both Masters and TServers. PR #1347. - Added
Config.LeaderElectionDisabledto 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.FetchOnlyKnownKindsto 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.ByPeriodnow derives a job's period from its effective scheduled time (InsertOpts.ScheduledAtwhen 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 andScheduledAtvalues in different time zones produce the same unique key for the same period. Unique keys for scheduledByPeriodjobs, and for anyByPeriodjob 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 considersExcludeKind, 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
JobRescueroverwriting 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, ordiscarded) and sorting by finalized time, including in River UI. PR #1374. - Fixed
rivermigrateleavingriver_migrationrows 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. WithMigrateTx, rows for every removed version were left behind. PR #1378. - Fixed
river benchinserting every benchmark job with anumarg of0instead of numbering jobs sequentially. PR #1379. - Attempt errors that are valid JSON but don't have the shape River writes (for example an
attimestamp in another format, anattemptstored as a string, or anerrorortracethat 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 leftrunningindefinitely. PR #1380. - A fetched job whose row can't be decoded (for example a SQLite job whose
tagswere changed to something other than an array of strings) no longer leaves every job locked in the same fetch stuckrunning. 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-arrayerrorsvalues 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
pendingout of band, or being rescued while the completer's update waited on the row lock (in which case PostgreSQL returns the job's pre-updaterunningrow). Such jobs are now skipped without emitting a completion event. PR #1383. - Fixed
JobListpagination skipping or repeating jobs when ordering byJobListOrderByTimewith 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, likefinalized_atfor unfinalized jobs, are paginated correctly and consistently sort last in ascending order and first in descending order on all drivers. Ordering byJobListOrderByTimewith an emptyStates()filter now usesscheduled_atinstead of returning an error. PR #1384. - A SQLite job whose
args,attempted_by,errors,metadata, ortagswere 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 invaliderrorsvalue is kept as a string in a new array so that attempt errors can still be appended. PR #1386. - Fixed
UniqueOpts.ByArgsskipping distinct jobs or failing inserts when JSON keys contain path syntax (likeuser.id), are empty, or come from unnamed tags likejson:",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.UnmarshalTextrejecting valid cursors whose URL-safe base64 encoding contains-or_, so cursors produced byMarshalTextalways 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 stucklog line reporting the client-levelJobTimeoutinstead of the worker-level timeout when a worker overridesTimeout. 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
JobCancelandJobCancelTxnotifying running workers through the shared control outbox, so their contexts are cancelled when the transaction commits. PR #1398. - Fixed SQLite
InsertManyandInsertManyTxreporting multiple inserted jobs when a batch contains the same active unique key more than once. Such batches now fail atomically, matching PostgreS...