Module: Sashiko::Context

Defined in:
lib/sashiko/context.rb

Overview

Helpers for propagating OTel Context across concurrency boundaries.

OpenTelemetry::Context uses fiber-local storage, so spans started in a new Thread or detached Fiber have no parent by default. These helpers capture the caller’s Context and re-attach it inside the forked unit of work, so parent/child span relationships survive the boundary.

Class Method Summary collapse

Class Method Details

.attach(carrier) ⇒ Object

Re-attach a context captured via #carrier. Yields with that context as current. The extracted context is “remote” from OTel’s POV, so spans started inside become children of the original span.



66
# File 'lib/sashiko/context.rb', line 66

def attach(carrier, &) = with(OpenTelemetry.propagation.extract(carrier), &)

.carrierObject

—- Cross-boundary propagation via W3C Trace Context —————

Serialize the current OTel Context into a plain Hash of string headers (traceparent, tracestate). The hash contains only strings, so it’s Ractor-shareable as-is, JSON-serializable, and safe to pass through any boundary:

- Sidekiq/ActiveJob job arguments
- Kafka/SQS message attributes
- Ractor.new(...) arguments
- HTTP request headers


57
58
59
60
61
# File 'lib/sashiko/context.rb', line 57

def carrier
  h = {} #: Hash[String, String]
  OpenTelemetry.propagation.inject(h)
  ::Ractor.make_shareable(h)
end

.currentObject

Yield the current OTel Context. Typically captured at submit time, then passed to #attach inside the worker.



14
# File 'lib/sashiko/context.rb', line 14

def current = OpenTelemetry::Context.current

.fiber(&block) ⇒ Object

Fiber.new { work } that preserves the current OTel Context. Returns the Fiber; caller resumes it.



29
30
31
32
# File 'lib/sashiko/context.rb', line 29

def fiber(&block)
  ctx = current
  Fiber.new { with(ctx, &block) }
end

.parallel_map(enumerable, &block) ⇒ Object

Map block over enumerable, each element executed on its own thread, all with the Context captured at call time. Returns [result, …] in input order. Useful for fanning out instrumented work (e.g. parallel tool calls, parallel HTTP) from inside a traced method.



38
39
40
41
42
43
44
45
# File 'lib/sashiko/context.rb', line 38

def parallel_map(enumerable, &block)
  ctx = current
  enumerable
    .map.with_index { |item, i| Thread.new { [i, with(ctx) { block.call(item) }] } }
    .map(&:value)
    .sort_by(&:first)
    .map(&:last)
end

.thread(&block) ⇒ Object

Thread.new { work } that preserves the current OTel Context. Returns the Thread so callers can .join / .value as usual.



22
23
24
25
# File 'lib/sashiko/context.rb', line 22

def thread(&block)
  ctx = current
  Thread.new { with(ctx, &block) }
end

.with(context) ⇒ Object

Run ‘block` with the given Context attached. Caller owns detach ordering — use #with for the auto-managed form.



18
# File 'lib/sashiko/context.rb', line 18

def with(context, &) = OpenTelemetry::Context.with_current(context, &)