Module: Hyperion::FiberLocal

Defined in:
lib/hyperion/fiber_local.rb

Overview

FiberLocal — tooling for fiber-local request scope under the Async scheduler.

## Background

Under fiber-per-request concurrency (Hyperion Phase 2+, Falcon), all in-flight requests share the same OS thread. Code that stores per-request state on ‘Thread.current` would leak between requests.

**Ruby 3.2+ already solves the most common case:** ‘Thread.current = v` writes to FIBER-local storage, not thread-local storage. Each fiber’s ‘Thread.current` is independent. This is what Hyperion relies on.

The remaining footgun is ‘Thread.current.thread_variable_set`, which IS genuinely thread-shared and will leak across fiber-scheduled requests. Old Rails code (< 7.0) sometimes used this. Modern Rails uses `ActiveSupport::IsolatedExecutionState` (which routes to Fiber storage), so well-maintained apps are not affected.

## What this module provides

‘Hyperion::FiberLocal.verify_environment!` — sanity check that the current Ruby actually isolates `Thread.current` per-fiber. Raises if not (which would only happen on Ruby < 3.2).

‘Hyperion::FiberLocal.install!` — opt-in monkey-patch that ALSO routes `thread_variable_get`/`thread_variable_set` to fiber storage. Use only if you know your app stores request scope via thread variables and you accept the trade-offs (genuine thread-pool patterns will break).

Class Method Summary collapse

Class Method Details

.install!Object

Opt-in patch that routes thread_variable_get/set to fiber storage. Most apps DO NOT need this — Ruby 3.2+ symbol-keyed Thread.current[] is already fiber-local. Only install! if your app uses thread_variable_set for request scope.



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/hyperion/fiber_local.rb', line 65

def install!
  return if @installed

  ::Thread.class_eval do
    alias_method :__hyperion_orig_tvar_get, :thread_variable_get
    alias_method :__hyperion_orig_tvar_set, :thread_variable_set

    define_method(:thread_variable_get) do |key|
      sym = key.to_sym
      storage = ::Fiber.current.storage
      return storage[sym] if storage&.key?(sym)

      __hyperion_orig_tvar_get(key)
    end

    define_method(:thread_variable_set) do |key, value|
      ::Fiber.current.storage ||= {}
      ::Fiber.current.storage[key.to_sym] = value
    end
  end

  @installed = true
end

.installed?Boolean

Returns:

  • (Boolean)


36
37
38
# File 'lib/hyperion/fiber_local.rb', line 36

def installed?
  @installed
end

.uninstall!Object

Test-only undo. Not promised for production.



90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/hyperion/fiber_local.rb', line 90

def uninstall!
  return unless @installed

  ::Thread.class_eval do
    alias_method :thread_variable_get, :__hyperion_orig_tvar_get
    alias_method :thread_variable_set, :__hyperion_orig_tvar_set
    remove_method :__hyperion_orig_tvar_get
    remove_method :__hyperion_orig_tvar_set
  end

  @installed = false
end

.verify_environment!Object

Confirm that the current Ruby treats Thread.current as fiber-local. Raises NotImplementedError on older Ruby where the leak still exists.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/hyperion/fiber_local.rb', line 42

def verify_environment!
  marker = :__hyperion_fiber_isolation_check__
  ::Thread.current[marker] = :outer

  observed = nil
  ::Fiber.new { observed = ::Thread.current[marker] }.resume

  unless observed.nil?
    raise NotImplementedError,
          'Thread.current[:k] is NOT fiber-local on this Ruby. ' \
          'Hyperion requires Ruby 3.2+ for safe fiber-per-request scope. ' \
          "Got Ruby #{RUBY_VERSION}."
  end

  true
ensure
  ::Thread.current[marker] = nil
end