Deadlines, budgets, and cancellation you can reason about in production.
Home · Documentation · Blog · Changelog · Report Bug · Request Feature · AI Skills · llms.txt · llms-full.txt
TIMEx
TIMEx is a deadline engine for Ruby: one facade runs your code under a Deadline, picks an execution strategy (cooperative checks, thread wakeup, IO deadlines, subprocesses, and more), and routes expiry through consistent on_timeout semantics—without pulling in a framework.
Note
Documentation reflects the latest code on main. For version-specific documentation, refer to the docs/ directory within that version's tag.
What you get
-
TIMEx.deadline/TIMEx.call— single entrypoint withstrategy:,on_timeout:,auto_check:, and strategy-specific options -
Deadline— monotonic + wall alignment, narrowing (#min), skew-aware header encoding (X-TIMEx-Deadline) -
Strategies —
:cooperative,:unsafe,:io,:wakeup,:subprocess,:closeable,:ractor(whenRactoris defined), each registered onTIMEx::Registry -
Composers —
TwoPhase,Hedged,Adaptivefor multi-attempt and staged execution -
on_timeout—:raise(default),:raise_standard,:return_nil,:result, or a customProcwith shared dispatch inTimeoutHandling -
Result— discriminated:ok/:timeout/:erroroutcomes when you opt out of raising -
Propagation —
Deadline#to_header/Deadline.from_headerplus optional Rack middleware for cross-service budgets -
Telemetry & clocks — pluggable
Telemetry.adapter, injectable monotonic/wallClock, andTIMEx::Test::VirtualClockfor tests - Rails (opt-in) — install generator adds initializer hooks without loading Rails from the core require
See the feature comparison for how TIMEx compares to Timeout.timeout and other patterns.
Requirements
- Ruby: MRI 3.3+ or a compatible JRuby/TruffleRuby release
- Runtime dependencies: none beyond the standard library (no ActiveSupport required)
Rails middleware and generators load only when you opt in after bundle install.
Installation
gem install timex
# - or -
bundle add timexQuick example
1. Budget
Pass seconds, a Deadline, or nil for an infinite budget. The block receives a frozen Deadline you can thread through helpers.
deadline = TIMEx::Deadline.in(2.5)
TIMEx.deadline(deadline) { |d| process!(d) }2. Run
The default :cooperative strategy runs your block and performs a final check! so CPU-bound work still observes expiry at cooperative points.
TIMEx.deadline(1.0) do |deadline|
rows = fetch_rows
deadline.check!
summarize(rows)
end3. On expiry
Override per call or via TIMEx.configure. Use :result when you want a TIMEx::Result instead of an exception.
outcome = TIMEx.deadline(0.01, on_timeout: :result, strategy: :unsafe) do
sleep 5 # interrupted when the budget is exhausted
end
outcome.timeout? # => true4. Propagate
Serialize remaining budget into an outbound request so downstream services share the same cap.
req["X-TIMEx-Deadline"] = TIMEx::Deadline.in(3.0).to_header
# or use TIMEx::Propagation::RackMiddleware on the server (see docs)Ready to go deeper? Start with Getting Started and Migrating from stdlib Timeout.
Contributing
Bug reports and pull requests are welcome at https://github.com/drexed/timex. We're committed to fostering a welcoming, collaborative community. Please follow our code of conduct.
License
The gem is available as open source under the terms of the LGPLv3 License.