I'm looking at using autotuner with a multi-threaded Rack server (Puma) and before I do I wanted to confirm my understanding of how RequestContext works across threads.
From reading the code, RequestCollector creates a single RequestContext instance at initialization, which is then used for every request through the middleware. In a multi-threaded server, I'd expect concurrent requests to share that same instance, meaning concurrent calls to before_request would overwrite @before_gc_context and @start_time_ms on each other:
Thread A: before_request() → @before_gc_context.update # snapshot at T1
Thread B: before_request() → @before_gc_context.update # snapshot at T2, overwrites T1
Thread A: after_request() → measures window [T2, end_of_A] instead of [T1, end_of_A]
Is that actually the behavior, or am I missing something? For example, is there a reason this is safe that isn't obvious from the code — perhaps because GC.stat is process-global anyway so the per-request window doesn't need to be precise?
I'm looking at using autotuner with a multi-threaded Rack server (Puma) and before I do I wanted to confirm my understanding of how RequestContext works across threads.
From reading the code, RequestCollector creates a single RequestContext instance at initialization, which is then used for every request through the middleware. In a multi-threaded server, I'd expect concurrent requests to share that same instance, meaning concurrent calls to before_request would overwrite @before_gc_context and @start_time_ms on each other:
Thread A: before_request() → @before_gc_context.update # snapshot at T1
Thread B: before_request() → @before_gc_context.update # snapshot at T2, overwrites T1
Thread A: after_request() → measures window [T2, end_of_A] instead of [T1, end_of_A]
Is that actually the behavior, or am I missing something? For example, is there a reason this is safe that isn't obvious from the code — perhaps because GC.stat is process-global anyway so the per-request window doesn't need to be precise?