Skip to content

[Ruby] Preserve encoded job args on insert - #1503

Draft
bgentry wants to merge 1 commit into
masterfrom
bg/ruby-preserve-encoded-args
Draft

bgentry wants to merge 1 commit into
masterfrom
bg/ruby-preserve-encoded-args

Conversation

@bgentry

@bgentry bgentry commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

This fixes how River Ruby stores job args on insert. Three of the four driver combinations (Active Record on Postgres, Active Record on SQLite, and Sequel on SQLite) decode a job's encoded args with JSON.parse and serialize them again before inserting. That round trip runs every number through Ruby's Float, so 1.10 becomes 1.1, 1e3 becomes 1000.0, and decimals with more digits than a Float holds are silently rounded. On SQLite, which otherwise keeps JSON as written, unicode escapes get decoded too. It affects args encoded by #to_json and args that plugins supply already encoded.

Inserts now pass the encoded bytes straight to the database. On Postgres, Active Record binds them as a literal wrapped in CAST(... AS jsonb), bypassing the jsonb attribute type that did the round trip. Sequel on Postgres already passed the string through. The stored value is Postgres's normal jsonb form of the input, the same as Go's River with pgx: 1.10 and long decimals stay exact, while 1e3 becomes 1000 and jsonb normalizes key order and whitespace as usual.

On SQLite, both drivers still insert a batch with one json_each statement, but each job's args go into the batch document as a JSON string instead of a parsed object. json_extract unwraps that string back to the original JSON text before jsonb parses it, so the stored args match what Go's SQLite driver stores for the same bytes. Unique keys were already computed from the encoded args, so they are unaffected.

Insert job args using the encoded bytes produced by `#to_json` (or
supplied pre-encoded, as by a plugin) rather than decoding them with
`JSON.parse` and serializing them again. That round trip runs on Active
Record with Postgres or SQLite and on Sequel with SQLite, and it
rewrites numbers through Ruby's `Float`: `1.10` becomes `1.1`, `1e3`
becomes `1000.0`, and decimals beyond `Float` precision are rounded.
On SQLite it also decodes unicode escapes, since the stored JSON is
otherwise kept as written.

On Postgres, Active Record now binds the encoded args as a SQL literal
wrapped in `CAST(... AS jsonb)`, bypassing the jsonb attribute type that
would decode and re-encode them. Postgres applies its normal jsonb
normalization, the same as Go's River drivers, so `1.10` and long
decimals are kept exactly. Sequel on Postgres already passes the string
through.

On SQLite, both drivers embed the encoded args as a JSON string in the
batch document. `json_extract` unwraps it to the original JSON text
before `jsonb` parses it, so the stored args match the bytes Go's
SQLite driver stores for the same input. Batch inserts stay a single
statement, and unique key computation already uses the encoded args, so
it is unaffected.
@bgentry
bgentry force-pushed the bg/ruby-preserve-encoded-args branch from 817378a to 504ffcb Compare October 14, 2026 01:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants