Module: Bitfab::ReplayContext

Defined in:
lib/bitfab/replay.rb

Overview

Replay context shared by fibers on the current thread. Ruby's Thread#[] storage is fiber-local, so use true thread variables here: lazy Enumerator bodies run in another fiber but must retain replay mocking.

Class Method Summary collapse

Class Method Details

.currentObject



54
55
56
# File 'lib/bitfab/replay.rb', line 54

def current
  Thread.current.thread_variable_get(REPLAY_CONTEXT_KEY)
end

.current_spanObject



58
59
60
# File 'lib/bitfab/replay.rb', line 58

def current_span
  current&.dig(:span_stack)&.last
end

.with_context(test_run_id:, input_source_span_id: nil, input_source_trace_id: nil, trace_id: nil, mock_tree: nil, mock_strategy: nil, mock_overrides: nil, fetch_span_output: nil, db_branch_lease: nil, db_branch_timings: nil, source_bitfab_trace_id: nil) ⇒ Object

Execute a block with replay context set for every fiber on the current thread. Child threads and processes intentionally do not inherit it.



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/bitfab/replay.rb', line 75

def with_context(test_run_id:, input_source_span_id: nil, input_source_trace_id: nil, trace_id: nil,
  mock_tree: nil, mock_strategy: nil, mock_overrides: nil, fetch_span_output: nil,
  db_branch_lease: nil, db_branch_timings: nil, source_bitfab_trace_id: nil)
  previous = Thread.current.thread_variable_get(REPLAY_CONTEXT_KEY)
  ctx = {
    test_run_id:,
    input_source_span_id:,
    input_source_trace_id:,
    trace_id:,
    span_stack: []
  }
  if mock_tree
    ctx[:mock_tree] = mock_tree
    ctx[:mock_strategy] = mock_strategy || "marked"
    ctx[:call_counters] = {}
    # Selective mock overrides ride alongside the tree: they gate on the
    # same non-root call-counter machinery, so they only fire when a tree
    # is present. process_single_item fetches the tree whenever overrides
    # exist, even under mock: "none".
    ctx[:mock_overrides] = mock_overrides if mock_overrides && !mock_overrides.empty?
    # Lazy per-span output fetcher for a payload-free non-"all" tree path.
    # Absent on the eager "all" path, whose outputs are inline even when
    # overrides are present.
    ctx[:fetch_span_output] = fetch_span_output if fetch_span_output
  end
  # The per-trace DB branch (resolved server-side) and the Bitfab trace ID
  # it belongs to ride on the context so ReplayBranch can read them inside
  # the replayed method.
  #
  # ReplayBranch also sets ctx[:db_snapshot_accessed] = true the first
  # time customer code actually obtains the branch URL for this item
  # (via +database_url+). Reported on the trace
  # completion inside the +db_snapshot_usage+ record (its +accessed+
  # field) so the server can distinguish "branch was provisioned and
  # exposed" from "branch URL was actually consumed". Only an explicit
  # +database_url+ read may set it. A path that hands the URL over by other
  # means (e.g. a process-isolated runner writing an env overlay) must
  # leave it alone: setting it there would make every such replay report
  # accessed for free, and the flag would stop separating "branch was
  # used" from "branch was offered".
  ctx[:db_branch_lease] = db_branch_lease if db_branch_lease
  # Kept off ReplayBranch: customer code reads that mid-replay to reach the
  # branch, and provisioning latency is a property of the run, not of the
  # connection. It rides the context only to be echoed on the completion.
  ctx[:db_branch_timings] = db_branch_timings if db_branch_timings
  ctx[:source_bitfab_trace_id] = source_bitfab_trace_id if source_bitfab_trace_id
  Thread.current.thread_variable_set(REPLAY_CONTEXT_KEY, ctx)
  yield
ensure
  Thread.current.thread_variable_set(REPLAY_CONTEXT_KEY, previous)
end

.with_span(trace_id:, span_id:) ⇒ Object



62
63
64
65
66
67
68
69
70
71
# File 'lib/bitfab/replay.rb', line 62

def with_span(trace_id:, span_id:)
  ctx = current
  return yield unless ctx

  entry = {trace_id:, span_id:}
  ctx[:span_stack].push(entry)
  yield
ensure
  ctx[:span_stack].pop if ctx
end