Commit 03db7f8
Fix HTTPX thread-safety issue with thread-local connections for incremental rendering (#2116)
## Problem
The HTTPX `stream_bidi` plugin has a **critical limitation**: only the
thread that creates the bidirectional streaming connection can write
chunks to the request stream. This causes race conditions in
multi-threaded production environments.
### Race Condition Scenario
1. **First request** (Thread A):
- Creates HTTPX connection
- Stores in `@incremental_connection` instance variable
- Works fine ✅
2. **Second request** (Thread B - different Puma worker thread):
- Tries to use same `@incremental_connection`
- Attempts to write chunks
- **FAILS** ❌ - HTTPX raises error because Thread B didn't create the
connection
### Root Cause
- Puma handles different HTTP requests on different threads
- HTTPX `stream_bidi` plugin requires thread affinity for writes
- Shared instance variable causes cross-thread access violations
## Solution
Implemented **thread-local storage** for incremental rendering
connections:
```ruby
def incremental_connection
Thread.current[:react_on_rails_incremental_connection] ||= create_incremental_connection
end
```
### How It Works
- Each Puma worker thread gets its own persistent bidirectional
streaming connection
- Stored in `Thread.current` instead of instance variable
- Automatically isolated between threads
- Proper cleanup via `reset_thread_local_incremental_connections`
## Changes
- Modified `incremental_connection` to use thread-local storage
- Added `reset_thread_local_incremental_connections` method
- Updated `reset_connection` to clean up all thread-local connections
- Removed shared `@incremental_connection` instance variable
## Trade-offs
### ✅ Benefits
- Eliminates race conditions completely
- Simple implementation using Ruby's built-in thread-local storage
- Each thread has isolated, persistent connection
- Works correctly in production with Puma
### 1 parent 8703f5d commit 03db7f8
File tree
4 files changed
+19
-9
lines changed- packages/react-on-rails-pro-node-renderer/src
- worker
- react_on_rails_pro/lib/react_on_rails_pro
4 files changed
+19
-9
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
315 | 315 | | |
316 | 316 | | |
317 | 317 | | |
318 | | - | |
| 318 | + | |
319 | 319 | | |
320 | 320 | | |
321 | 321 | | |
322 | 322 | | |
323 | 323 | | |
324 | 324 | | |
325 | 325 | | |
326 | | - | |
| 326 | + | |
327 | 327 | | |
328 | 328 | | |
329 | 329 | | |
| |||
Lines changed: 3 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
| 8 | + | |
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| |||
93 | 93 | | |
94 | 94 | | |
95 | 95 | | |
96 | | - | |
| 96 | + | |
97 | 97 | | |
98 | 98 | | |
99 | 99 | | |
100 | | - | |
| 100 | + | |
101 | 101 | | |
102 | 102 | | |
103 | 103 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
365 | 365 | | |
366 | 366 | | |
367 | 367 | | |
368 | | - | |
| 368 | + | |
369 | 369 | | |
370 | 370 | | |
371 | 371 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
13 | | - | |
14 | 13 | | |
15 | | - | |
| 14 | + | |
16 | 15 | | |
17 | 16 | | |
18 | 17 | | |
| |||
135 | 134 | | |
136 | 135 | | |
137 | 136 | | |
| 137 | + | |
| 138 | + | |
138 | 139 | | |
139 | | - | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
140 | 150 | | |
141 | 151 | | |
142 | 152 | | |
| |||
0 commit comments