Skip to content

Ruby: generate default client ID per process - #1498

Merged
bgentry merged 1 commit into
masterfrom
bg/ruby-per-process-client-id
Oct 12, 2026
Merged

bgentry merged 1 commit into
masterfrom
bg/ruby-per-process-client-id

Conversation

@bgentry

@bgentry bgentry commented Oct 11, 2026

Copy link
Copy Markdown
Contributor

River::Config currently builds its default client ID (host-pid-random) when the config is constructed. Preforking servers like Puma, Unicorn, and Pitchfork with application preloading construct the config before forking, so every worker process ends up sharing one client ID. Anything keyed by client ID then conflates sibling processes: attempted_by, leader election, and producer bookkeeping such as River Pro's dead-producer rescue, where a live sibling can vouch for a dead sibling's jobs. Go clients always get a unique ID per process.

This generates the default ID lazily on first use in each process. Config#id compares the current Process.pid against the PID that generated the cached ID and regenerates it in a forked child. A PID check needs no global Process._fork hook, covers every fork path, and matches the per-process handling the Rails integration already uses. Every reader in a process (Config#id, Client#id, job claims, leadership, plugins) sees the same stable ID, and host names are truncated so a generated ID always fits the 127-character limit.

An explicit id: is used unchanged in every process, keeping Go's contract that callers who set IDs keep them unique. Copies made with Config#with share the original's ID unless they override id:.

New specs fork child processes that build clients from a shared config and check that each child gets a distinct ID tied to its PID, that explicit IDs survive forks, that the ID is stable across threads, and that claims, leadership, and accessors agree on it.

@bgentry
bgentry force-pushed the bg/ruby-per-process-client-id branch from 97bde35 to 8faf2f2 Compare October 11, 2026 22:49

@brandur brandur left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, WFM.

`River::Config` builds its default client ID (`host-pid-random`) when
the config is constructed. Preforking servers such as Puma, Unicorn, or
Pitchfork with application preloading construct the config before they
fork, so every worker process inherits the same ID. Anything keyed by
client ID then conflates sibling processes: `attempted_by`, leader
election, and producer bookkeeping such as River Pro's dead-producer
rescue, where a live sibling can vouch for a dead sibling's jobs. Go
clients always get a unique ID per process.

Generate the default ID lazily, on first use in each process. `Config#id`
compares the current `Process.pid` against the PID that generated the
cached ID and regenerates it in a forked child. A PID check needs no
global `Process._fork` hook, covers every fork path, and matches the
per-process client handling already used by the Rails integration. The
cached PID and ID are swapped in together as one frozen value behind a
mutex, so every reader in a process (`Config#id`, `Client#id`, job
claims, leadership, and plugins such as River Pro) sees the same stable
ID. Host names are truncated so the generated ID always fits the
127-character limit.

An explicit `id:` is used unchanged in every process, preserving the Go
contract that callers who set IDs keep them unique. Copies made with
`Config#with` share the original's ID, including its per-process
default, unless the copy overrides `id:`.

New specs fork child processes that build clients from a shared config
and verify that each child gets a distinct ID tied to its PID, that the
parent's ID is unchanged, that explicit IDs survive forks, that the ID
is stable across threads, and that claims, leadership, and accessors
all agree on the generated ID.
@bgentry
bgentry force-pushed the bg/ruby-per-process-client-id branch from 8faf2f2 to 18efcc8 Compare October 12, 2026 20:55
@bgentry
bgentry marked this pull request as ready for review October 12, 2026 20:55
@bgentry
bgentry enabled auto-merge (squash) October 12, 2026 20:55
@bgentry
bgentry merged commit 97b0ef1 into master Oct 12, 2026
33 checks passed
@bgentry
bgentry deleted the bg/ruby-per-process-client-id branch October 12, 2026 20:59
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