fix(context): use a Fiber attribute for Context #1807
+1,076
−8
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Alternative to #1760
This PR implements
Context
storage using an attribute added toFiber
. This is inspired by Rails' implementation ofIsolatedExecutionState
. It adds 3 tests that fail with alternative implementations ofContext
storage. The tests represent the sort of Fiber-local storage and Thread-based Fiber-local variable manipulation performed by things likeActionController::Live
. The approach used in this PR is a form of "security via obscurity" - side-stepping this kind of brute-force manipulation of Fiber-scoped data by stashing our data elsewhere. It won't prevent malicious manipulation ofFiber.current.opentelemetry_context
, of course.A benchmark is included with this PR exploring the design space with 8 different implementations of
Context
. Most of these, including the most performant implementations, are not safe in the presence ofActionController::Live
, but they're included for comparison.Results with Ruby 3.4.1 (note that this PR,
FiberAttributeContext
, is a performance increase over the existing implementation,ArrayContext
):The recursive benchmark measures 10 nested calls to
Context.with_value
, similar to a typical uses of nested spans (OpenTelemetry.tracer.in_span('foo') { ... }
), which utilizeContext.with_value
.Implementations:
FiberLocalArrayContext
- mutable array held in aFiber[STACK_KEY]
that corrects for shared ownership afterFiber.new
(see below). Unfortunately, this trickery permits the owning Fiber to mutate the shared array afterFiber.new
before the newFiber
uses it. It was a nice try, though. This is unsafe in the presence of bulk manipulation ofFiber#storage
.FiberLocalLinkedListContext
- linked list held inFiber[STACK_KEY]
. This is unsafe in the presence of bulk manipulation ofFiber#storage
.FiberAttributeContext
- mutable array held inFiber.current.opentelemetry_context
. This is the implementation proposed in this PR and is the most performant "safe" implementation.ArrayContext
- mutable array held inThread.current[STACK_KEY]
. This is unsafe in the presence of bulk manipulation ofThread#[]
. This is the existing implementation.LinkedListContext
- linked list held inThread.current[STACK_KEY]
. This is unsafe in the presence of bulk manipulation ofThread#[]
.ImmutableArrayContext
- immutable array held inThread.current[STACK_KEY]
. This is unsafe in the presence of bulk manipulation ofThread#[]
. This is the implementation in fix(context): do not modify stack array directly when attach and detach #1760.FiberLocalImmutableArrayContext
- immutable array held in aFiber[STACK_KEY]
. This is unsafe in the presence of bulk manipulation ofFiber#storage
.FiberLocalVarContext
- mutable array held in aConcurrent::FiberLocalVar
. This is unsafe in the presence of bulk manipulation ofFiber#storage
.Tricky implementation of Fiber-local
Stack
that isn't quite safe enough: