Issue #19078 has been updated by ivoanjo (Ivo Anjo).
marcotc (Marco Costa) wrote in #note-14:
Regarding threads I think we shouldn't inherit
automatically in new threads, and rather do it explicitly (via Fiber.current.storage=) in
the rare cases it's needed.
I'm on the fence on this one. Usually the code spawning a thread, and the code using
thread/fiber storage and needing it to be inherited are entirely decoupled. So you'd
need to go convince all your dependencies that spawn threads (e.g. puma, sidekiq, etc) to
add that one line of code. So I'd prefer if it was always implicitly copied.
For cross-cutting concerns, like telemetry, automatic inheritance works best; asking a
gem user to add one extra line per Fiber created would create room for error.
I think thinking about the opposite use case, users that explicitly want a clean Fiber
storage for newly created Fibers, would help here: how common is such use case? I
can't think of a reason to have this as the default, except for saving on the
performance cost of copying the storage.
To a bit of info on top of this (and disclaimer, I work with Marco [on the ddtrace
gem](https://github.com/datadog/dd-trace-rb), one really nice property of the automatic
inheritance is that it makes easy something that is otherwise quite hard to do
automatically from regular Ruby code. E.g. to simulate such an automatic mechanism, one
needs to monkey patch thread and fiber creation, which is really awkward and error-prone
(ask me how I know this).
----------------------------------------
Feature #19078: Introduce `Fiber#storage` for inheritable fiber-scoped variables.
https://bugs.ruby-lang.org/issues/19078#change-100334
* Author: ioquatix (Samuel Williams)
* Status: Open
* Priority: Normal
* Assignee: ioquatix (Samuel Williams)
----------------------------------------
Pull Request:
https://github.com/ruby/ruby/pull/6612
This is an evolution of the previous ideas:
-
https://bugs.ruby-lang.org/issues/19058
-
https://bugs.ruby-lang.org/issues/19062
This PR introduces fiber scoped variables, and is a solution for problems like
<https://github.com/ioquatix/ioquatix/discussions/17>.
The main interface is:
```ruby
Fiber[key] = value
Fiber[key] # => value
```
The variables are scoped (local to) a fiber and inherited into child fibers and threads.
```ruby
Fiber[:request_id] = SecureRandom.hex(16)
Fiber.new do
p Fiber[:request_id] # prints the above request id
end
```
The fiber scoped variables are stored and can be accessed:
```ruby
Fiber.current.storage # => returns a Hash (copy) of the internal storage.
Fiber.current.storage= # => assigns a Hash (copy) to the internal storage.
```
Fiber itself has one new keyword argument:
```
Fiber.new(..., storage: hash, false, undef, nil)
```
This can control how the fiber variables are setup in a child context.
To minimise the performance overhead of some of the implementation choices, we are also
simultaneously implementing <https://bugs.ruby-lang.org/issues/19077>.
## Examples
### Request loop
```ruby
Thread.new do
while request = queue.pop
Fiber.new(storage: {id: SecureRandom.hex(16)}) do
handle_request.call(request)
end
end
end
```
OR
```ruby
Thread.new do
while request = queue.pop
Fiber.current.storage = {id: SecureRandom.hex(16)}
handle_request.call(request)
end
end
```
--
https://bugs.ruby-lang.org/