Module: Apartment::Patches::LiveTenantPropagation
- Defined in:
- lib/apartment/patches/live_tenant_propagation.rb
Overview
Backports rails/rails#56902 (“Pass IsolatedExecutionState.context to share_with”) to Rails versions where the fix has not landed.
On Rails 7.2 / 8.0 / 8.1.x stable, ActionController::Live#process calls ActiveSupport::IsolatedExecutionState.share_with(Thread.current, …) which reads from Thread.current.active_support_execution_state. Under :fiber isolation that’s empty — the data lives on Fiber.current’s accessor. The spawned thread’s root fiber starts with empty state and any CurrentAttributes set on the request fiber are invisible inside response.stream.write — silent cross-tenant data exposure.
The fix: before Live#process spawns the streaming thread, point Thread.current.active_support_execution_state at the same hash Fiber.current.active_support_execution_state points at. share_with’s shallow .dup then copies the right data into the spawned thread’s root fiber. Restore Thread’s prior state on exit.
Mechanically equivalent to what rails/rails#56902 does on main: capture IsolatedExecutionState.context (= Fiber.current under :fiber) and pass it to share_with. Apartment can’t edit Rails source from inside a gem, but a temporary mirror feeds share_with the right pointer with the same end result.
No-op under :thread isolation (Rails’ own propagation already works there). No-op when Fiber.current.active_support_execution_state is nil (no CurrentAttributes have been touched on this fiber yet, so there is nothing to propagate).
See docs/designs/rails-boundary-tenancy.md.
Instance Method Summary collapse
Instance Method Details
#process(name) ⇒ Object
37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/apartment/patches/live_tenant_propagation.rb', line 37 def process(name) return super unless ActiveSupport::IsolatedExecutionState.isolation_level == :fiber fiber_state = Fiber.current.active_support_execution_state return super if fiber_state.nil? previous_thread_state = Thread.current.active_support_execution_state Thread.current.active_support_execution_state = fiber_state begin super ensure Thread.current.active_support_execution_state = previous_thread_state end end |