contribsys/einhorn
70.4
Strong · 19 September 2026
2.7k
lines of production code
Ruby
primary language
1
measurement over time
What this system is
Einhorn is a process manager for Ruby applications that handles worker lifecycle, socket binding, and graceful upgrades. It exposes bound sockets and command interfaces to workers via environment variables and supports non-interactive scripting. The system ensures reliability through robust signal handling, state migration, and configurable timeout mechanisms for stuck processes.
How it got here
2012 — Einhorn 1.0 modernization and stability
12 changes.
This period focused on modernizing the Einhorn codebase to require Ruby 2.5+, introducing explicit socket binding APIs, and migrating the internal command protocol from JSON to YAML. Significant efforts were made to improve reliability through robust signal handling, safe deserialization, and comprehensive unit testing with Minitest.
2014–2016 — integration test suite expansion
6 changes.
This period focused on building a comprehensive integration test suite for Einhorn, specifically targeting the worker upgrade process, environment variable handling, and signal management. The work involved creating helper modules and various fixture scripts to simulate complex scenarios such as re-execution, process death signals, and environment cleanup during upgrades.
Behavioural changes
Einhorn 1.0.0: Modernized codebase, Ruby 2.5+ requirement, and updated socket API
This release updates Einhorn to version 1.0.0, dropping support for Ruby versions below 2.5 and requiring Ruby 2.5.0 for code formatting via the \standard\ gem. The project now uses \YAML.safe\_load\ for compatibility with Ruby 3.1+, removes the \fiddle\ gem as a hard dependency (making it optional for the \-k\ flag), and adds \logger\ and \readline\ for Ruby 4.0 support. For users, the socket binding API has changed: the \-b\ flag now explicitly binds addresses (e.g., \-b 127.0.0.1:1234\), and file descriptors are exposed via \EINHORN\_FD\_N\ and \EINHORN\_FD\_COUNT\ environment variables instead of the previous \EINHORN\_FDS\ format. The command socket file descriptor is now passed via \EINHORN\_SOCK\_FD\ (using the \-g\ flag) rather than \EINHORN\_FD\. Additionally, new options include \--reexec-as\ for custom upgrade commands, \--drop-env-var\ to clean the environment, and \--nice\ for process priority control.
(repo-wide) · high confidence
Einhorn 1.1.1: Protocol migration, Linux process management, and upgrade reliability
Einhorn upgrades to version 1.1.1, introducing several behavioral changes to improve reliability and compatibility. The command-socket protocol switches from JSON to YAML, requiring clients to use the new \send\_command\ and \receive\_message\ methods. On Linux, Einhorn now uses \prctl\ to set the parent-death signal for workers, ensuring they are cleaned up if the master process dies. Upgrade behavior is enhanced with a configurable \--signal-timeout\ that escalates to SIGKILL for stuck workers, and the system now tracks consecutive unacked worker deaths to detect spin-up failures. Additionally, workers receive a unique index via \EINHORN\_CHILD\_INDEX\, and the environment variable for the command socket has changed from \EINHORN\_FD\ to \EINHORN\_SOCK\_FD\.
lib/einhorn · high confidence
Einhorn introduces explicit socket binding, renames command-socket flag, and adds non-interactive shell execution
The Einhorn process manager now uses a new \-b ADDR\ (or \--bind ADDR\) option to explicitly specify server socket addresses and ports, replacing the previous implicit parsing of command arguments like \srv:IP:PORT\; bound sockets are exposed to workers via \EINHORN\_FD\_N\ environment variables. The existing flag to expose the command socket as a file descriptor has been renamed from \-b\ to \-g\ (or \--command-socket-as-fd\), and the corresponding environment variable is now \EINHORN\_SOCK\_FD\. Additionally, \einhornsh\ now supports a new \-e CMD\_LINE\_SEQ\ (or \--execute\) option, allowing users to run command sequences non-interactively, which facilitates scripting and automation outside of the interactive REPL.
bin · high confidence
Improved socket error handling and subscription tracking in event connections
The event handling layer now robustly manages unexpected socket errors (such as ECONNRESET) by logging them and closing connections, rather than failing silently or crashing. Additionally, client connections now track subscriptions, allowing them to be restored during upgrades for backward compatibility. Logging for client connection/disconnection events has been demoted from info to debug level to reduce noise.
lib/einhorn/event · high confidence
Signal handling moved to event loop and command parsing hardened
Signal handlers (INT, TERM, QUIT, HUP, USR2) now run asynchronously via the event loop instead of directly in the signal context, preventing race conditions during worker signaling and state changes. Additionally, the command interface now validates incoming messages using the transport layer's deserializer, returning a 'Could not parse command' error for invalid input instead of crashing.
lib/einhorn/command · high confidence
State schema expansion and safe YAML deserialization
The internal state structure has been expanded to expose bound ports, support smooth upgrades, track last upgrade times, and manage worker nice levels, while the command-socket protocol now uses SafeYAML for deserialization to prevent unsafe object loading. Additionally, the system now automatically migrates state formats across reloads by adding new keys and removing obsolete ones, ensuring compatibility between different Einhorn versions during upgrades.
lib · high confidence
Updated examples to use new Einhorn socket API and EventMachine-LE
The example scripts (thin\_example, time\_server, pool\_worker) have been updated to reflect changes in the Einhorn worker interface. The examples now use the \Einhorn::Worker.socket!\ method and \Einhorn::Worker.einhorn\_fd\_count\ instead of parsing command-line arguments or environment variables directly, and they require \eventmachine-le\ and \thin/attach\_socket\ to handle socket attachment. Additionally, the \pool\_worker\ example now uses \loop do\ instead of \while true\ for its main work loop.
example · high confidence
Test coverage
Added env\_printer fixture for integration testing; Added integration test fixture for server exit during upgrade; Added integration test helpers for Einhorn; Added integration tests for Einhorn upgrade, environment, and signal handling; Added pdeathsig\_printer test fixture; Added test fixture for Einhorn worker upgrade scenarios; Added unit tests for Einhorn client, worker pool, and command logic; Added unit tests for state updates and preload behavior; Migrate command interface tests to Minitest and YAML serialization; Migrate test suite from Test::Unit/Shoulda to Minitest.
Dependencies
Modernize gemspec and update development dependencies
The einhorn gemspec has been updated to require Ruby 2.5.0 or higher, explicitly set the MIT license, and add metadata URIs for bug tracking and documentation. Development dependencies have been refreshed: shoulda and mocha are replaced by minitest (\~\> 5) and mocha (\~\> 2), with rake (\~\> 13) and subprocess (\~\> 1) added. Runtime dependencies now explicitly include the logger and readline gems, while the JSON dependency has been removed. The Gemfile also adds standard and debug gems for the development and test groups.
(dependencies) · high confidence
Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.
How this codebase got here
Baseline
- First survey — no prior run to compare against. CAI 70.
Lenses
- Code Health 96
- Architecture 100
- Maturity 54
- Readiness 72
- Security 99
Changes since last survey
- 300 commits — 276 feature/other, 24 fixes
By area
- lib/einhorn — 115 commits
- (repo) — 51 commits
- (root) — 47 commits
- lib/einhorn.rb — 23 commits
- test/integration — 23 commits
- bin/einhorn — 22 commits
- test/unit — 9 commits
- bin/einhornsh — 5 commits
- .github/workflows — 2 commits
- example/plugin.rb — 2 commits
- example/time_server — 1 commit
Notable commits
- fix: Add programmatic specification of the license. Fixes #26
- fix: Add ruby 2.0 fix to bundler
- fix: Fix --quiet
- fix: Fix PID rollover bug
- fix: Fix UNIXSocket leak
- fix: Fix broken Einhorn image URL
- fix: Fix deprecation warnings in Ruby and Bundler API usage
- fix: Fix error message copy
- fix: Fix invalid file extension
- fix: Fix rake tests
- fix: Fix regression with closing file descriptors before exec
- fix: Fix several testsuite warnings
- fix: Fix the docs to match actual behavior of EINHORN_FD_N vars
- fix: Fix the tests on 1.9.2
- fix: Fix variable name
- fix: Last round of 1.8.7 fixes: IO::WaitWritable -> Errno::EINPROGRESS
- fix: Merge pull request #116 from rpeng/rpeng/fix-ruby-34-string-lit-warning
- fix: Merge pull request #13 from ConradIrwin/bug/--quiet
- fix: Merge pull request #20 from ebroder/fix-envvar-docs
- fix: Merge pull request #21 from ConradIrwin/bug/slow-death
- …and 280 more
Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.
Survey your own repository
contribsys/einhorn was measured the same way every project in this corpus was: the same rubric, at a pinned commit, with the result published in full. Point a surveyor at a repository you know and see whether you agree with it.
About this page
- The score is its most recent published measurement, taken on 19 September 2026 at a pinned commit. It is not a live figure and does not change until the project is measured again.
- Measured at commit ac1b7a2a67bfb44d7be0783b3ff55eeea5700148 — the exact code this score is about.
- Scored under rubric-2026.09.15 — the same rubric and the same method as every other entry in this index.
- Measured by watchdog.canine.dev using codehealth-analyzer preprod-13a154b7f5d1.