Project

General

Profile

Actions

Feature #19056

open

Introduce `Fiber.annotation` for attaching messages to fibers.

Added by ioquatix (Samuel Williams) over 1 year ago. Updated 10 months ago.

Status:
Open
Target version:
-
[ruby-core:110293]

Description

It's useful to know what a fiber is doing especially when they have a temporal execution (i.e. sockets connecting vs connected, binding vs accepting, queue popping, etc)

Let's introduce Fiber.annotate and Fiber#annotation for logging a short message attached to Fibers.

Fiber.annotate "Counting to 10"
10.times{|I| puts I}

# Fiber.current.annotation => "Counting to 10"

Pull Request: https://github.com/ruby/ruby/pull/6554


Files

clipboard-202210160132-n7lzp.png (865 KB) clipboard-202210160132-n7lzp.png ioquatix (Samuel Williams), 10/15/2022 12:32 PM
Actions #1

Updated by ioquatix (Samuel Williams) over 1 year ago

  • Tracker changed from Bug to Feature
  • Backport deleted (2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN)
Actions #2

Updated by ioquatix (Samuel Williams) over 1 year ago

  • Description updated (diff)

Updated by ioquatix (Samuel Williams) over 1 year ago

A counter-proposal to introduce Fiber#name, but this is different concept.

fiber = Fiber.current
fiber.name = "Processor"
while item = queue.pop
  fiber.annotation = "Processing item #{item}..."
  # ... work ...
end

In this case, Fiber#annotation is related to the current activity of the Fiber, not its general name in relation to other fibers. However, I'm not against introducing Fiber#name in another proposal.

This feature is already part of Async::Task and is very useful for debugging programs with a lot of fibers, since we can visualise the activity of all the different fibers easily. I'll share a screenshot and/or video.

Updated by ioquatix (Samuel Williams) over 1 year ago

Here is a picture of an interactive debugger (async-debug) which uses similar annotations to present useful information about the state of the system.

@Eregon (Benoit Daloze) proposed that there should be a way to switch it on and off. I don't have a strong opinion about it- I've not run into any performance issues with Async::Task#annotation.

That being said, we could introduce something like Fiber.annotate{..} which is only evaluated if annotations are switched on (e.g. Fiber.annotations = true/false).

Updated by matz (Yukihiro Matsumoto) over 1 year ago

For the record, I write down the concerns:

  • I don't see the convenience of Fiber.annotate. Convince me.
  • Users need to understand how to use Fiber#annotation wisely. Need documents.
  • The state should be stacked, not overridden. How do you think?
  • I have a bit of performance concern. Is it OK?

Matz.

Updated by mame (Yusuke Endoh) over 1 year ago

I think annotation per Fiber will cause conflict in usage between libraries. For example, consider the following library code.

def fetch_info_from_url(uri)
  Fiber.new do
    Fiber.annotate "fetching info from #{ uri }"
    URI.open(uri) do |f|
      f.each_line do |line|
        Fiber.yield line
      end
    end
  end
end

f = fetch_info_from_uri("example.com")
p f.resume   #=> first line of example.com
f.annotation #=> "fetching info from example.com"

It should work perfect. And then, suppose uri gem has been updated to use Fiber.annotate in URI.open(uri).

def URI.open(...)
  ...
  Fiber.annotate "calling the user block"
  yield io
  ...
end

Now f.annotation will return "calling the user block" instead of "fetching info from example.com".

What is the bad thing here? Must uri gem not use Fiber.annotate? If so, what is allowed to use it?

Also, the message "calling the user block" is correct in the context of uri gem, but not very informative for the application authors. However, it is difficult for uri gem to write a more appropriate message because it does not know the context of the entire application.

After all, only the application authors can use Fiber.annotate, not library authors. If so, there seems little need to introduce it in the core.

Updated by ioquatix (Samuel Williams) over 1 year ago

I want to add annotations to default gems like Net::HTTP so it would need to be in core, or at least some kind of hook. If it's too much to have a specific implementation, we could just make a general annotation method and then hook it up with a callback to the fiber scheduler or something.

Another use case: https://github.com/socketry/async/issues/78

Updated by ioquatix (Samuel Williams) 10 months ago

I came back to this issue.

I don't see the convenience of Fiber.annotate. Convince me.

  • Logging what the current fiber is doing.
  • Recording what the current fiber is doing when an error or exception occurs.
  • Real time debugger output (e.g. as shown in the screenshot).

Users need to understand how to use Fiber#annotation wisely. Need documents.

Yes, I am happy to include documentation about it.

The state should be stacked, not overridden. How do you think?

What about:

Fiber.annotate "x" do
  Fiber.annotation # "x"
  Fiber.annotate "y" do
    Fiber.annotation # "y"
  end
  Fiber.annotation # "x" 
end

I have a bit of performance concern. Is it OK?

It's a single pointer assignment so it should be minimal. Compared to "blocking operations" it should be irrelevant.

In order to try this out more generally, I created a gem. You can see the proposed implementation here: https://github.com/ioquatix/fiber-annotate/blob/main/lib/fiber/annotate.rb

The reason to make this a core interface, is so that default gems can use it.

Updated by Eregon (Benoit Daloze) 10 months ago

Why not use a fiber-local variable for this? Then there is much less risk of conflict between libraries, and of course you can define convenience methods as you like.
Or as you implemented it in that gem, as an attribute + annotate(message) { ... }.

IMO this is good to be in a gem. I don't think it needs to be in core.

Updated by ioquatix (Samuel Williams) 10 months ago

If it's not in core, can we still use it in net-http and other gems like that?

Updated by Eregon (Benoit Daloze) 10 months ago

Probably not, but I also think net-http shouldn't use it.
There is overhead to e.g. call Addrinfo#inspect (or #to_s) in your screenshot above.
Also for such logging to be useful one most likely needs string interpolation, which means string allocations even when the annotations are not used.
And non-Fiber users probably have little to no use for those annotations.

Updated by ioquatix (Samuel Williams) 10 months ago

I understand your concerns.

Every production system I've worked on has some kind of APM, and I've never heard anyone complain about the overhead - it's usually minuscule in comparison to the actual work being done.

I'm fine for it to be a gem, but it will limit the application to core libraries. Careful application of such a feature can be incredibly helpful for debugging.

One way to mitigate the performance is to use things like prepend to inject annotations to existing code. But it's clunky at best.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0