https://github.com/drexed/timex
A Swiss-army knife for timeouts in Ruby.
https://github.com/drexed/timex
Last synced: 9 days ago
JSON representation
A Swiss-army knife for timeouts in Ruby.
- Host: GitHub
- URL: https://github.com/drexed/timex
- Owner: drexed
- License: other
- Created: 2026-05-14T21:50:16.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-05-14T21:52:57.000Z (about 1 month ago)
- Last Synced: 2026-05-14T23:38:45.160Z (about 1 month ago)
- Language: Ruby
- Size: 1.29 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.txt
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
---
Deadlines, budgets, and cancellation you can reason about in production.
[Home](https://drexed.github.io/timex) ·
[Documentation](https://drexed.github.io/timex/getting_started) ·
[Blog](https://drexed.github.io/timex/blog) ·
[Changelog](./CHANGELOG.md) ·
[Report Bug](https://github.com/drexed/timex/issues) ·
[Request Feature](https://github.com/drexed/timex/issues) ·
[AI Skills](https://github.com/drexed/timex/blob/main/skills) ·
[llms.txt](https://drexed.github.io/timex/llms.txt) ·
[llms-full.txt](https://drexed.github.io/timex/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](https://drexed.github.io/timex/getting_started/) 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 with `strategy:`, `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` (when `Ractor` is defined), each registered on `TIMEx::Registry`
- **Composers** — `TwoPhase`, `Hedged`, `Adaptive` for multi-attempt and staged execution
- **`on_timeout`** — `:raise` (default), `:raise_standard`, `:return_nil`, `:result`, or a custom `Proc` with shared dispatch in `TimeoutHandling`
- **`Result`** — discriminated `:ok` / `:timeout` / `:error` outcomes when you opt out of raising
- **Propagation** — `Deadline#to_header` / `Deadline.from_header` plus optional Rack middleware for cross-service budgets
- **Telemetry & clocks** — pluggable `Telemetry.adapter`, injectable monotonic/wall `Clock`, and `TIMEx::Test::VirtualClock` for tests
- **Rails (opt-in)** — install generator adds initializer hooks without loading Rails from the core require
See the [feature comparison](https://drexed.github.io/timex/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
```sh
gem install timex
# - or -
bundle add timex
```
## Quick example
### 1. Budget
Pass seconds, a `Deadline`, or `nil` for an infinite budget. The block receives a frozen `Deadline` you can thread through helpers.
```ruby
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.
```ruby
TIMEx.deadline(1.0) do |deadline|
rows = fetch_rows
deadline.check!
summarize(rows)
end
```
### 3. On expiry
Override per call or via `TIMEx.configure`. Use `:result` when you want a `TIMEx::Result` instead of an exception.
```ruby
outcome = TIMEx.deadline(0.01, on_timeout: :result, strategy: :unsafe) do
sleep 5 # interrupted when the budget is exhausted
end
outcome.timeout? # => true
```
### 4. Propagate
Serialize remaining budget into an outbound request so downstream services share the same cap.
```ruby
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](https://drexed.github.io/timex/getting_started/) and [Migrating from stdlib `Timeout`](https://drexed.github.io/timex/migrating_from_stdlib_timeout/).
## Contributing
Bug reports and pull requests are welcome at . We're committed to fostering a welcoming, collaborative community. Please follow our [code of conduct](CODE_OF_CONDUCT.md).
## License
The gem is available as open source under the terms of the [LGPLv3 License](https://www.gnu.org/licenses/lgpl-3.0.html).