bitfab 0.29.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/bitfab/client.rb +21 -9
- data/lib/bitfab/http_client.rb +7 -1
- data/lib/bitfab/replay.rb +149 -69
- data/lib/bitfab/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1711bd0806a246ec36f15e2bedd0b31d8fe8c9df0aacc922d7ad15a20badf629
|
|
4
|
+
data.tar.gz: 2e619cf976e2909bdf1bf629cb350e6b761115d4ec9d8372beaacfe5b46adbbf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 88d118025460c8aed53e48d4ca16815133c67aa04986688bf1ed84ed506a16c41f11e76fd012cb9c18f6ee06a44f1b42e7de811e78834da737b9e4b260d5a024
|
|
7
|
+
data.tar.gz: 6c0e746e23dfdf5995c502e3a40cc12c03888c5e783b924049d2cd4152660c8a226b9ce852532440687630449fe84f027a75082602f175619fbe28e38121e9ba
|
data/lib/bitfab/client.rb
CHANGED
|
@@ -83,13 +83,18 @@ module Bitfab
|
|
|
83
83
|
# replay runs into a single experiment batch
|
|
84
84
|
# @param dataset_id [String, nil] optional UUID of the dataset this replay
|
|
85
85
|
# runs against, stored on the resulting experiment for durable attribution
|
|
86
|
+
# @param grader_ids [Array<String>, nil] optional UUIDs of graders attached
|
|
87
|
+
# directly to this experiment, graded as the union with the dataset's
|
|
88
|
+
# runnable graders at completion; each must be an active/live grader in the
|
|
89
|
+
# same org and trace function or the server rejects the replay
|
|
86
90
|
# @param mock [String] mock strategy for child spans: "marked" (default),
|
|
87
91
|
# "none", or "all". "marked" only mocks spans declared with
|
|
88
92
|
# mock_on_replay: true; "all" mocks every child span.
|
|
89
93
|
# @param adapt_inputs [#call, nil] optional hook to reshape recorded inputs
|
|
90
94
|
# onto the method's current signature when its shape changed after the
|
|
91
95
|
# traces were captured. Receives (args, kwargs, ctx) where ctx is
|
|
92
|
-
# {
|
|
96
|
+
# { original_trace_id:, original_span_id: } (with deprecated
|
|
97
|
+
# source_trace_id/source_span_id aliases), and returns [new_args, new_kwargs].
|
|
93
98
|
# @param mock_override [Hash, Array<Hash>, nil] optional selective mock
|
|
94
99
|
# override(s), each a { match:, value: } hash. match is a callable:
|
|
95
100
|
# match.call(node) selects spans to substitute (node is
|
|
@@ -108,12 +113,12 @@ module Bitfab
|
|
|
108
113
|
# A raising callback never crashes the run.
|
|
109
114
|
# @return [Hash] with :items, :test_run_id, :test_run_url
|
|
110
115
|
def replay(receiver, method_name, trace_function_key:, limit: nil, trace_ids: nil, max_concurrency: 10,
|
|
111
|
-
name: nil, code_change_description: nil, code_change_files: nil, experiment_group_id: nil, dataset_id: nil, mock: "marked",
|
|
116
|
+
name: nil, code_change_description: nil, code_change_files: nil, experiment_group_id: nil, dataset_id: nil, grader_ids: nil, mock: "marked",
|
|
112
117
|
adapt_inputs: nil, mock_override: nil, environment: nil, on_progress: nil)
|
|
113
118
|
Replay.run(
|
|
114
119
|
self, receiver, method_name,
|
|
115
120
|
trace_function_key:, limit:, trace_ids:, name:, max_concurrency:,
|
|
116
|
-
code_change_description:, code_change_files:, experiment_group_id:, dataset_id:, mock:, adapt_inputs:,
|
|
121
|
+
code_change_description:, code_change_files:, experiment_group_id:, dataset_id:, grader_ids:, mock:, adapt_inputs:,
|
|
117
122
|
mock_override:, environment:,
|
|
118
123
|
on_progress:
|
|
119
124
|
)
|
|
@@ -375,6 +380,9 @@ module Bitfab
|
|
|
375
380
|
{
|
|
376
381
|
neon_branch_id: lease["neonBranchId"],
|
|
377
382
|
snapshot_timestamp: lease["snapshotTimestamp"],
|
|
383
|
+
original_trace_id: replay_ctx[:source_bitfab_trace_id],
|
|
384
|
+
# Deprecated wire alias, kept so this SDK still reports usage
|
|
385
|
+
# against servers that predate the rename.
|
|
378
386
|
source_trace_id: replay_ctx[:source_bitfab_trace_id],
|
|
379
387
|
accessed: replay_ctx[:db_snapshot_accessed] == true
|
|
380
388
|
}
|
|
@@ -542,10 +550,11 @@ module Bitfab
|
|
|
542
550
|
# db_snapshot_usage: replay DB branch usage record, present only when a
|
|
543
551
|
# lease was attached to the replay item. Serialized as `db_snapshot_usage`
|
|
544
552
|
# on the raw trace so the server can stamp the trace's metadata at ingest:
|
|
545
|
-
# { neon_branch_id:, snapshot_timestamp: (optional),
|
|
546
|
-
# (optional), accessed: } with
|
|
547
|
-
# the branch URL and false if it
|
|
548
|
-
# lease was attached, in which
|
|
553
|
+
# { neon_branch_id:, snapshot_timestamp: (optional), original_trace_id:
|
|
554
|
+
# (optional, with its deprecated source_trace_id alias), accessed: } with
|
|
555
|
+
# :accessed true if customer code obtained the branch URL and false if it
|
|
556
|
+
# ignored it. nil outside replay or when no lease was attached, in which
|
|
557
|
+
# case the key is omitted entirely.
|
|
549
558
|
def send_trace_completion(trace_function_key:, trace_id:, started_at:, ended_at:, db_snapshot_usage: nil)
|
|
550
559
|
trace_state = TraceState.get(trace_id)
|
|
551
560
|
trace_started_at = trace_state&.dig(:started_at) || started_at
|
|
@@ -573,8 +582,11 @@ module Bitfab
|
|
|
573
582
|
if db_snapshot_usage[:snapshot_timestamp]
|
|
574
583
|
usage["snapshot_timestamp"] = db_snapshot_usage[:snapshot_timestamp]
|
|
575
584
|
end
|
|
576
|
-
if db_snapshot_usage[:
|
|
577
|
-
usage["
|
|
585
|
+
if db_snapshot_usage[:original_trace_id]
|
|
586
|
+
usage["original_trace_id"] = db_snapshot_usage[:original_trace_id]
|
|
587
|
+
# Deprecated wire alias, kept so this SDK still reports usage
|
|
588
|
+
# against servers that predate the rename.
|
|
589
|
+
usage["source_trace_id"] = db_snapshot_usage[:original_trace_id]
|
|
578
590
|
end
|
|
579
591
|
usage["accessed"] = db_snapshot_usage[:accessed]
|
|
580
592
|
raw_trace["db_snapshot_usage"] = usage
|
data/lib/bitfab/http_client.rb
CHANGED
|
@@ -112,8 +112,13 @@ module Bitfab
|
|
|
112
112
|
# experiment/test run
|
|
113
113
|
# @param dataset_id [String, nil] optional UUID of the dataset this replay
|
|
114
114
|
# runs against, stored on the resulting experiment for durable attribution
|
|
115
|
+
# @param grader_ids [Array<String>, nil] optional UUIDs of graders attached
|
|
116
|
+
# directly to this experiment, graded as the union with the dataset's
|
|
117
|
+
# runnable graders at completion; each must be an active/live grader in the
|
|
118
|
+
# same org and trace function or the server rejects the replay
|
|
115
119
|
def start_replay(trace_function_key, limit, trace_ids: nil, code_change_description: nil,
|
|
116
|
-
code_change_files: nil, experiment_group_id: nil, name: nil, include_db_branch_lease: false, dataset_id: nil
|
|
120
|
+
code_change_files: nil, experiment_group_id: nil, name: nil, include_db_branch_lease: false, dataset_id: nil,
|
|
121
|
+
grader_ids: nil)
|
|
117
122
|
payload = {
|
|
118
123
|
"traceFunctionKey" => trace_function_key
|
|
119
124
|
}
|
|
@@ -127,6 +132,7 @@ module Bitfab
|
|
|
127
132
|
payload["experimentGroupId"] = experiment_group_id unless experiment_group_id.nil?
|
|
128
133
|
payload["includeDbBranchLease"] = true if include_db_branch_lease
|
|
129
134
|
payload["datasetId"] = dataset_id unless dataset_id.nil?
|
|
135
|
+
payload["graderIds"] = grader_ids unless grader_ids.nil?
|
|
130
136
|
|
|
131
137
|
# When DB branching is on, the server resolves a Neon preview branch per
|
|
132
138
|
# item (snapshot + restore + poll), which can run several seconds each.
|
data/lib/bitfab/replay.rb
CHANGED
|
@@ -104,27 +104,36 @@ module Bitfab
|
|
|
104
104
|
# replay runs into a single experiment batch
|
|
105
105
|
# @param dataset_id [String, nil] optional UUID of the dataset this replay
|
|
106
106
|
# runs against, stored on the resulting experiment for durable attribution
|
|
107
|
+
# @param grader_ids [Array<String>, nil] optional UUIDs of graders attached
|
|
108
|
+
# directly to this experiment, graded as the union with the dataset's
|
|
109
|
+
# runnable graders at completion; each must be an active/live grader in the
|
|
110
|
+
# same org and trace function or the server rejects the replay
|
|
107
111
|
# @param mock [String] mock strategy for child spans: "marked" (default),
|
|
108
112
|
# "none", or "all". "marked" only mocks spans declared with
|
|
109
113
|
# mock_on_replay: true; "all" mocks every child span.
|
|
110
114
|
# @param adapt_inputs [#call, nil] optional hook to reshape recorded inputs
|
|
111
115
|
# onto the method's current signature when its shape changed after the
|
|
112
116
|
# traces were captured. Receives (args, kwargs, ctx) where ctx is
|
|
113
|
-
# {
|
|
114
|
-
#
|
|
115
|
-
#
|
|
117
|
+
# { original_trace_id:, original_span_id: } (with deprecated
|
|
118
|
+
# source_trace_id/source_span_id aliases), and returns [new_args, new_kwargs].
|
|
119
|
+
# Runs per item inside the same rescue as the method, so a raising adapter
|
|
120
|
+
# sets that item's :error rather than crashing the run.
|
|
116
121
|
# @param on_progress [#call, nil] optional callback invoked once per item as
|
|
117
122
|
# it finishes, with a running-totals hash { completed:, total:, succeeded:,
|
|
118
|
-
# errored:, item: } where item is { trace_id:,
|
|
119
|
-
# the single item that just
|
|
120
|
-
#
|
|
123
|
+
# errored:, item: } where item is { trace_id:, original_trace_id:,
|
|
124
|
+
# original_span_id:, error:, duration_ms: } for the single item that just
|
|
125
|
+
# settled (source_trace_id/source_span_id remain as deprecated aliases).
|
|
126
|
+
# trace_id is the new server replay trace id, written in after the run
|
|
127
|
+
# completes (nil during progress callbacks); original_trace_id is the
|
|
128
|
+
# ORIGINAL (historical) trace that was replayed, error is that item's replay
|
|
129
|
+
# error or nil, and
|
|
121
130
|
# duration_ms is how long that one trace took to replay. Use it to
|
|
122
131
|
# render replay progress (e.g. a per-trace log). A raising callback never
|
|
123
132
|
# crashes the run.
|
|
124
133
|
# @return [Hash] with :items, :test_run_id, :test_run_url
|
|
125
134
|
def run(client, receiver, method_name, trace_function_key:, limit: nil, trace_ids: nil, name: nil,
|
|
126
135
|
max_concurrency: 10, code_change_description: nil, code_change_files: nil, experiment_group_id: nil,
|
|
127
|
-
dataset_id: nil, mock: "marked",
|
|
136
|
+
dataset_id: nil, grader_ids: nil, mock: "marked",
|
|
128
137
|
adapt_inputs: nil, mock_override: nil, environment: nil, on_progress: nil)
|
|
129
138
|
unless MOCK_STRATEGIES.include?(mock.to_s)
|
|
130
139
|
raise ArgumentError, "Invalid mock strategy '#{mock}'. Must be one of: #{MOCK_STRATEGIES.join(", ")}"
|
|
@@ -179,7 +188,8 @@ module Bitfab
|
|
|
179
188
|
code_change_files:,
|
|
180
189
|
experiment_group_id:,
|
|
181
190
|
include_db_branch_lease:,
|
|
182
|
-
dataset_id
|
|
191
|
+
dataset_id:,
|
|
192
|
+
grader_ids:
|
|
183
193
|
)
|
|
184
194
|
test_run_id = replay_data["testRunId"]
|
|
185
195
|
test_run_url = replay_data["testRunUrl"]
|
|
@@ -194,10 +204,9 @@ module Bitfab
|
|
|
194
204
|
|
|
195
205
|
# Every item joined its own trace-persistence threads (span uploads +
|
|
196
206
|
# completion) in execute_item, so all replay traces are on the server
|
|
197
|
-
# by now: no flush needed
|
|
198
|
-
#
|
|
199
|
-
#
|
|
200
|
-
# loudly.
|
|
207
|
+
# by now: no flush needed. complete_replay finalizes the run and returns
|
|
208
|
+
# the token/diagnostic mapping; its failures propagate loudly because a
|
|
209
|
+
# run that never completed can't be finalized.
|
|
201
210
|
complete_response = http_client.complete_replay(test_run_id)
|
|
202
211
|
trace_id_map = complete_response&.dig("traceIds")
|
|
203
212
|
# Per-replay-trace token usage keyed by server trace id: the REPLAYED
|
|
@@ -205,66 +214,85 @@ module Bitfab
|
|
|
205
214
|
# item's :tokens.
|
|
206
215
|
replay_tokens = complete_response&.dig("tokens") || {}
|
|
207
216
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
# but the server has no record: a nil trace_id blocks verdict
|
|
218
|
-
# persistence and the Studio experiments view downstream, so this
|
|
219
|
-
# must never be silent.
|
|
220
|
-
#
|
|
221
|
-
# Severity splits on scope:
|
|
222
|
-
# - ALL completed items missing: systemic (the replayed method is
|
|
223
|
-
# not traced, or uploads are wholesale broken). Raise; the run's
|
|
224
|
-
# results are unusable for persistence.
|
|
225
|
-
# - SOME completed items missing: per-item upload failure (transient
|
|
226
|
-
# network blip, one oversized payload). Nil those items and warn
|
|
227
|
-
# loudly, but return the run so callers can persist verdicts for
|
|
228
|
-
# the items that landed.
|
|
217
|
+
# trace_id_map maps each item's client-side correlation id (:_sdk_trace_id,
|
|
218
|
+
# which tagged that item's spans during the run) to the server's trace row
|
|
219
|
+
# id. We use it to write the real server replay trace id into item[:trace_id]
|
|
220
|
+
# now that the row exists, attach each item's server-aggregated token usage,
|
|
221
|
+
# and detect a systemic upload failure early. Verdict persistence does NOT
|
|
222
|
+
# use this map: it is keyed by the original-trace lineage (:original_trace_id +
|
|
223
|
+
# test_run_id), which needs no client-held server id. Older servers that omit
|
|
224
|
+
# the map yield no replay tokens and leave item[:trace_id] nil.
|
|
225
|
+
unless trace_id_map.nil?
|
|
229
226
|
missing = []
|
|
230
227
|
completed_count = 0
|
|
231
228
|
result_items.each do |item|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
229
|
+
local_id = item[:_sdk_trace_id]
|
|
230
|
+
mapped = local_id && trace_id_map[local_id]
|
|
231
|
+
# Write the real server replay trace id in as it comes back; the item
|
|
232
|
+
# held nil until now (the client correlation id is never surfaced).
|
|
233
|
+
item[:trace_id] = mapped
|
|
235
234
|
if item[:error].nil?
|
|
236
235
|
completed_count += 1
|
|
237
|
-
missing <<
|
|
236
|
+
missing << local_id if mapped.nil?
|
|
238
237
|
end
|
|
239
|
-
# Pull this item's replayed-run tokens by its server trace id, before
|
|
240
|
-
# :trace_id is overwritten with that id below.
|
|
241
238
|
item[:tokens] = normalize_tokens(replay_tokens[mapped]) if mapped
|
|
242
|
-
item[:trace_id] = mapped
|
|
243
239
|
end
|
|
244
|
-
|
|
240
|
+
# ALL completed items missing: systemic (the replayed method is not
|
|
241
|
+
# traced, or uploads are wholesale broken). Raise; the run's traces
|
|
242
|
+
# never persisted, so nothing can be labeled.
|
|
243
|
+
if completed_count.positive? && missing.length == completed_count
|
|
245
244
|
trace_count = complete_response["traceCount"]
|
|
246
245
|
server_count = trace_count.nil? ? "" : " The server persisted #{trace_count} trace(s) for this run."
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
246
|
+
raise "Replay completed but the server has no persisted trace for " \
|
|
247
|
+
"any of the #{completed_count} completed item(s) " \
|
|
248
|
+
"(test_run_id #{test_run_id}).#{server_count} Trace uploads were " \
|
|
249
|
+
"joined, so either the uploads failed or the replayed method is " \
|
|
250
|
+
"not traced (no root span was emitted)."
|
|
251
|
+
end
|
|
252
|
+
# SOME completed items missing: per-item upload failure. Warn, but
|
|
253
|
+
# return the run; the items that landed can still be labeled.
|
|
254
|
+
if missing.any?
|
|
254
255
|
warn "Bitfab: server has no persisted trace for #{missing.length} of " \
|
|
255
256
|
"#{completed_count} completed replay item(s) " \
|
|
256
|
-
"(test_run_id #{test_run_id})
|
|
257
|
-
"and
|
|
257
|
+
"(test_run_id #{test_run_id}). Their replay token usage is " \
|
|
258
|
+
"unavailable and they cannot be labeled."
|
|
258
259
|
end
|
|
259
260
|
end
|
|
260
261
|
|
|
261
|
-
|
|
262
|
+
# Strip the internal correlation handle so returned items expose only the
|
|
263
|
+
# public shape (runs against older servers that omit the map too).
|
|
264
|
+
result_items.each { |item| item.delete(:_sdk_trace_id) }
|
|
265
|
+
|
|
266
|
+
result = {
|
|
262
267
|
items: result_items,
|
|
263
268
|
test_run_id:,
|
|
264
269
|
test_run_url: "#{client.service_url}#{test_run_url}"
|
|
265
270
|
}
|
|
266
|
-
|
|
267
|
-
|
|
271
|
+
# Persist the enriched result two ways so the Bitfab plugin never has to
|
|
272
|
+
# parse the replay's stdout (which a dependency's logging can corrupt):
|
|
273
|
+
# write it to BITFAB_REPLAY_RESULT_PATH when the plugin set that env var,
|
|
274
|
+
# and stream a terminal "complete" progress event. The plugin prefers the
|
|
275
|
+
# streamed event and falls back to the file. The event routes through
|
|
276
|
+
# on_progress so only progress-reporting runs emit it.
|
|
277
|
+
write_replay_result_file(result)
|
|
278
|
+
if on_progress
|
|
279
|
+
errored = result_items.count { |item| !item[:error].nil? }
|
|
280
|
+
total = result_items.length
|
|
281
|
+
begin
|
|
282
|
+
on_progress.call({
|
|
283
|
+
type: "complete",
|
|
284
|
+
test_run_id:,
|
|
285
|
+
completed: total,
|
|
286
|
+
total:,
|
|
287
|
+
succeeded: total - errored,
|
|
288
|
+
errored:,
|
|
289
|
+
result:
|
|
290
|
+
})
|
|
291
|
+
rescue => e
|
|
292
|
+
warn "Bitfab: replay on_progress callback raised: #{e.message}"
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
result
|
|
268
296
|
end
|
|
269
297
|
|
|
270
298
|
def write_replay_result_file(result)
|
|
@@ -296,11 +324,13 @@ module Bitfab
|
|
|
296
324
|
succeeded = 0
|
|
297
325
|
errored = 0
|
|
298
326
|
# Each event carries the single item that just settled so a progress UI
|
|
299
|
-
# can render per-trace pass/fail as the run streams.
|
|
300
|
-
#
|
|
301
|
-
#
|
|
302
|
-
#
|
|
303
|
-
|
|
327
|
+
# can render per-trace pass/fail as the run streams. The item's :trace_id
|
|
328
|
+
# is nil at this stage: the server replay trace id isn't known until run()
|
|
329
|
+
# writes it in after complete_replay, and the client correlation id is
|
|
330
|
+
# never surfaced. original_trace_id (the historical trace being replayed,
|
|
331
|
+
# taken from the server item) is what a UI keys on to identify what just
|
|
332
|
+
# settled. source_trace_id/source_span_id are kept as deprecated aliases.
|
|
333
|
+
report = lambda do |result, original_trace_id, original_span_id, test_run_id|
|
|
304
334
|
return unless on_progress
|
|
305
335
|
|
|
306
336
|
progress_mutex.synchronize do
|
|
@@ -311,8 +341,12 @@ module Bitfab
|
|
|
311
341
|
on_progress.call({
|
|
312
342
|
test_run_id:, completed:, total:, succeeded:, errored:,
|
|
313
343
|
item: {
|
|
314
|
-
trace_id:
|
|
315
|
-
|
|
344
|
+
trace_id: result[:trace_id],
|
|
345
|
+
original_trace_id:,
|
|
346
|
+
original_span_id:,
|
|
347
|
+
# Deprecated aliases for original_trace_id/original_span_id.
|
|
348
|
+
source_trace_id: original_trace_id,
|
|
349
|
+
source_span_id: original_span_id,
|
|
316
350
|
input: result[:input],
|
|
317
351
|
result: result[:result],
|
|
318
352
|
original_output: result[:original_output],
|
|
@@ -333,7 +367,7 @@ module Bitfab
|
|
|
333
367
|
server_items.map do |item|
|
|
334
368
|
result = process_single_item(http_client, item, receiver, method_name, test_run_id, mock_strategy,
|
|
335
369
|
adapt_inputs, include_db_branch_lease, mock_overrides:)
|
|
336
|
-
report.call(result, item
|
|
370
|
+
report.call(result, original_trace_id_of(item), original_span_id_of(item), test_run_id)
|
|
337
371
|
result
|
|
338
372
|
end
|
|
339
373
|
else
|
|
@@ -351,7 +385,7 @@ module Bitfab
|
|
|
351
385
|
result = process_single_item(http_client, item, receiver, method_name, test_run_id, mock_strategy,
|
|
352
386
|
adapt_inputs, include_db_branch_lease, mock_overrides:)
|
|
353
387
|
results_mutex.synchronize { results[idx] = result }
|
|
354
|
-
report.call(result, item
|
|
388
|
+
report.call(result, original_trace_id_of(item), original_span_id_of(item), test_run_id)
|
|
355
389
|
end
|
|
356
390
|
end
|
|
357
391
|
end
|
|
@@ -370,6 +404,11 @@ module Bitfab
|
|
|
370
404
|
def process_single_item(http_client, server_item, receiver, method_name, test_run_id, mock_strategy,
|
|
371
405
|
adapt_inputs = nil, include_db_branch_lease = false, mock_overrides: [])
|
|
372
406
|
metrics = extract_server_item_metrics(server_item)
|
|
407
|
+
# The ORIGINAL (historical) trace/span this item replays. Canonical
|
|
408
|
+
# keys are originalTraceId/originalSpanId; older servers send them under
|
|
409
|
+
# the deprecated sourceTraceId/sourceSpanId aliases (see *_of helpers).
|
|
410
|
+
original_trace_id = original_trace_id_of(server_item)
|
|
411
|
+
original_span_id = original_span_id_of(server_item)
|
|
373
412
|
# The server resolves a Neon preview branch per item during /replay/start
|
|
374
413
|
# (only when include_db_branch_lease was sent). Release it in the +ensure+
|
|
375
414
|
# below so any raise (span fetch, mock-tree build, or the replayed
|
|
@@ -378,7 +417,7 @@ module Bitfab
|
|
|
378
417
|
# lease (env.active? is false for those).
|
|
379
418
|
lease = include_db_branch_lease ? server_item["dbBranchLease"] : nil
|
|
380
419
|
|
|
381
|
-
span = http_client.get_external_span(
|
|
420
|
+
span = http_client.get_external_span(original_span_id)
|
|
382
421
|
item_data = extract_span_data(span)
|
|
383
422
|
|
|
384
423
|
# Fetch the span tree when the base strategy needs recorded outputs
|
|
@@ -396,7 +435,7 @@ module Bitfab
|
|
|
396
435
|
mock_tree = nil
|
|
397
436
|
if mock_strategy == "all" || mock_strategy == "marked" || overrides_present
|
|
398
437
|
begin
|
|
399
|
-
tree = http_client.get_span_tree(
|
|
438
|
+
tree = http_client.get_span_tree(original_span_id, include_outputs:)
|
|
400
439
|
mock_tree = build_mock_tree(tree["root"] || {})
|
|
401
440
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
402
441
|
raise if e.is_a?(SystemExit) || e.is_a?(SignalException)
|
|
@@ -418,7 +457,13 @@ module Bitfab
|
|
|
418
457
|
# override's get_original_output).
|
|
419
458
|
fetch_span_output = (mock_tree && !include_outputs) ? build_span_output_fetcher(http_client) : nil
|
|
420
459
|
|
|
421
|
-
adapt_ctx = {
|
|
460
|
+
adapt_ctx = {
|
|
461
|
+
original_trace_id:,
|
|
462
|
+
original_span_id:,
|
|
463
|
+
# Deprecated aliases for original_trace_id/original_span_id.
|
|
464
|
+
source_trace_id: original_trace_id,
|
|
465
|
+
source_span_id: original_span_id
|
|
466
|
+
}
|
|
422
467
|
|
|
423
468
|
execute_item(
|
|
424
469
|
item_data,
|
|
@@ -435,11 +480,11 @@ module Bitfab
|
|
|
435
480
|
adapt_inputs:,
|
|
436
481
|
adapt_ctx:,
|
|
437
482
|
db_branch_lease: lease,
|
|
438
|
-
source_bitfab_trace_id:
|
|
483
|
+
source_bitfab_trace_id: original_trace_id,
|
|
439
484
|
db_snapshot_ref: server_item["dbSnapshotRef"]
|
|
440
485
|
)
|
|
441
486
|
rescue => e
|
|
442
|
-
warn "Bitfab: replay item for span #{
|
|
487
|
+
warn "Bitfab: replay item for span #{original_span_id} failed before execution: #{e.message}"
|
|
443
488
|
{
|
|
444
489
|
input: [],
|
|
445
490
|
result: nil,
|
|
@@ -449,12 +494,28 @@ module Bitfab
|
|
|
449
494
|
tokens: metrics&.dig(:tokens),
|
|
450
495
|
model: metrics&.dig(:model),
|
|
451
496
|
trace_id: nil,
|
|
497
|
+
original_trace_id:,
|
|
498
|
+
original_span_id:,
|
|
499
|
+
# Deprecated aliases for original_trace_id/original_span_id.
|
|
500
|
+
source_trace_id: original_trace_id,
|
|
501
|
+
source_span_id: original_span_id,
|
|
452
502
|
db_snapshot_ref: server_item["dbSnapshotRef"]
|
|
453
503
|
}
|
|
454
504
|
ensure
|
|
455
505
|
release_db_branch_lease(http_client, lease) if lease
|
|
456
506
|
end
|
|
457
507
|
|
|
508
|
+
# The ORIGINAL (historical) trace/span an item replays. Canonical server
|
|
509
|
+
# keys are originalTraceId/originalSpanId; older servers send them under
|
|
510
|
+
# the deprecated sourceTraceId/sourceSpanId aliases.
|
|
511
|
+
def original_trace_id_of(server_item)
|
|
512
|
+
server_item["originalTraceId"] || server_item["sourceTraceId"]
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
def original_span_id_of(server_item)
|
|
516
|
+
server_item["originalSpanId"] || server_item["sourceSpanId"]
|
|
517
|
+
end
|
|
518
|
+
|
|
458
519
|
# Delete the per-item Neon preview branch. Best-effort: a failure is warned
|
|
459
520
|
# but never raised: the server-side TTL janitor reaps orphans.
|
|
460
521
|
def release_db_branch_lease(http_client, lease)
|
|
@@ -583,6 +644,10 @@ module Bitfab
|
|
|
583
644
|
|
|
584
645
|
fn_result = nil
|
|
585
646
|
fn_error = nil
|
|
647
|
+
# Client-side correlation id that tags this item's replay spans so the
|
|
648
|
+
# server can echo back the row id it minted (resolved in run()'s
|
|
649
|
+
# complete-replay loop). Carried on the item under :_sdk_trace_id, never
|
|
650
|
+
# surfaced as the public :trace_id.
|
|
586
651
|
sdk_trace_id = SecureRandom.uuid
|
|
587
652
|
# Collects the root span's persistence threads (span uploads + trace
|
|
588
653
|
# completion). Joined below so this item's trace is on the server
|
|
@@ -607,7 +672,13 @@ module Bitfab
|
|
|
607
672
|
# supplied. Inside the rescue so a raising adapter surfaces on this
|
|
608
673
|
# item's :error instead of crashing the run; args is reported on :input.
|
|
609
674
|
if adapt_inputs
|
|
610
|
-
ctx = adapt_ctx || {
|
|
675
|
+
ctx = adapt_ctx || {
|
|
676
|
+
original_trace_id: nil,
|
|
677
|
+
original_span_id: input_source_span_id,
|
|
678
|
+
# Deprecated aliases for original_trace_id/original_span_id.
|
|
679
|
+
source_trace_id: nil,
|
|
680
|
+
source_span_id: input_source_span_id
|
|
681
|
+
}
|
|
611
682
|
args, kwargs = adapt_inputs.call(args, kwargs, ctx)
|
|
612
683
|
end
|
|
613
684
|
fn_result = if kwargs.empty?
|
|
@@ -633,7 +704,16 @@ module Bitfab
|
|
|
633
704
|
duration_ms: metrics[:duration_ms],
|
|
634
705
|
tokens: metrics[:tokens],
|
|
635
706
|
model: metrics[:model],
|
|
636
|
-
|
|
707
|
+
# Written in by run() from the complete-replay response once the server
|
|
708
|
+
# has minted this replay trace's row. Nil until then: the client-side
|
|
709
|
+
# correlation id (below) is never surfaced as the public :trace_id.
|
|
710
|
+
trace_id: nil,
|
|
711
|
+
_sdk_trace_id: sdk_trace_id,
|
|
712
|
+
original_trace_id: source_bitfab_trace_id,
|
|
713
|
+
original_span_id: input_source_span_id,
|
|
714
|
+
# Deprecated aliases for original_trace_id/original_span_id.
|
|
715
|
+
source_trace_id: source_bitfab_trace_id,
|
|
716
|
+
source_span_id: input_source_span_id,
|
|
637
717
|
db_snapshot_ref:
|
|
638
718
|
}
|
|
639
719
|
end
|
data/lib/bitfab/version.rb
CHANGED