bitfab 0.36.3 → 0.36.4
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 +14 -8
- data/lib/bitfab/replay.rb +52 -23
- data/lib/bitfab/version.rb +1 -1
- data/lib/bitfab.rb +7 -9
- 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: 5578e8fdcdd81610b4e65fa94a5e9be88339e816742f6ba7fea81b53867eedf5
|
|
4
|
+
data.tar.gz: 308d35969ac643d1ddcfb76c169a6108906c958275b392f7d159ea647b0bf63c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2c0511909223595a7792e780d0c77b8c0f5ef2d239e4b2a5dda7946a63fbe4801893ae02ba3392b74520ad9d82844e519d3319a900f22c30bef4857e616c0420
|
|
7
|
+
data.tar.gz: 0ae83e2f731bc2f6fa9f564b6fe8754b7fff82c5ae03a914067cca6fb75f4ddb10e935bba8976de6f0dabe7025812e49d9392e2ace61b5202c02d80b21fe00aa
|
data/lib/bitfab/client.rb
CHANGED
|
@@ -132,21 +132,27 @@ module Bitfab
|
|
|
132
132
|
# +:min_cu+/+:max_cu+ size the branch compute in Neon Compute Units and
|
|
133
133
|
# +:warmup_sql+ warms its cache. Read the resolved branch inside the
|
|
134
134
|
# replayed method with +Bitfab.current_replay_branch+.
|
|
135
|
-
# @param
|
|
136
|
-
# it finishes, with a running-totals hash
|
|
137
|
-
# errored: }.
|
|
138
|
-
#
|
|
139
|
-
# items whose method ran (:succeeded) from items
|
|
140
|
-
# A raising callback never crashes the run.
|
|
135
|
+
# @param on_item_finish [#call, nil] optional callback invoked exactly once
|
|
136
|
+
# per item as it finishes, always with a running-totals hash containing
|
|
137
|
+
# { completed:, total:, succeeded:, errored:, item: }. It never receives a
|
|
138
|
+
# whole-run completion event. Replay does not know pass/fail yet, so the
|
|
139
|
+
# totals only distinguish items whose method ran (:succeeded) from items
|
|
140
|
+
# that raised (:errored). A raising callback never crashes the run.
|
|
141
|
+
# @param on_progress [#call, nil] deprecated compatibility callback. It
|
|
142
|
+
# receives the same per-item events plus the legacy item-less +"complete"+
|
|
143
|
+
# event. Ignored when +on_item_finish+ is also provided.
|
|
144
|
+
# @param on_item_start [#call, nil] optional callback invoked when a worker
|
|
145
|
+
# begins processing each item. Pair it with on_item_finish to distinguish
|
|
146
|
+
# queued work from in-flight work. A raising callback never crashes the run.
|
|
141
147
|
# @return [Hash] with :items, :test_run_id, :test_run_url
|
|
142
148
|
def replay(receiver, method_name, trace_function_key:, limit: nil, trace_ids: nil, max_concurrency: 10,
|
|
143
149
|
name: nil, code_change_description: Replay::CODE_CHANGE_UNSET, code_change_files: Replay::CODE_CHANGE_UNSET, experiment_group_id: nil, dataset_id: nil, grader_ids: nil, mock: "marked",
|
|
144
|
-
adapt_inputs: nil, mock_override: nil, db_branch: nil, on_progress: nil)
|
|
150
|
+
adapt_inputs: nil, mock_override: nil, db_branch: nil, on_item_start: nil, on_item_finish: nil, on_progress: nil)
|
|
145
151
|
Replay.run(
|
|
146
152
|
self, receiver, method_name,
|
|
147
153
|
trace_function_key:, limit:, trace_ids:, name:, max_concurrency:,
|
|
148
154
|
code_change_description:, code_change_files:, experiment_group_id:, dataset_id:, grader_ids:, mock:, adapt_inputs:,
|
|
149
|
-
mock_override:, db_branch:,
|
|
155
|
+
mock_override:, db_branch:, on_item_start:, on_item_finish:,
|
|
150
156
|
on_progress:
|
|
151
157
|
)
|
|
152
158
|
end
|
data/lib/bitfab/replay.rb
CHANGED
|
@@ -201,11 +201,11 @@ module Bitfab
|
|
|
201
201
|
# and ceiling in Neon Compute Units, and +:warmup_sql+ warms the branch's
|
|
202
202
|
# cache before the replayed method sees it. Read the resolved branch
|
|
203
203
|
# inside the method with +Bitfab.current_replay_branch+.
|
|
204
|
-
# @param
|
|
204
|
+
# @param on_item_finish [#call, nil] optional callback invoked once per item as
|
|
205
205
|
# it finishes, with a running-totals hash { completed:, total:, succeeded:,
|
|
206
206
|
# errored:, item: } where item is { trace_id:, original_trace_id:,
|
|
207
207
|
# original_span_id:, error:, duration_ms: } for the single item that just
|
|
208
|
-
#
|
|
208
|
+
# finished (source_trace_id/source_span_id remain as deprecated aliases).
|
|
209
209
|
# trace_id is the new server replay trace id, written in after the run
|
|
210
210
|
# completes (nil during progress callbacks); original_trace_id is the
|
|
211
211
|
# ORIGINAL (historical) trace that was replayed, error is that item's replay
|
|
@@ -213,14 +213,22 @@ module Bitfab
|
|
|
213
213
|
# duration_ms is how long that one trace took to replay. Use it to
|
|
214
214
|
# render replay progress (e.g. a per-trace log). A raising callback never
|
|
215
215
|
# crashes the run.
|
|
216
|
+
# @param on_progress [#call, nil] deprecated compatibility callback. It
|
|
217
|
+
# receives the same per-item finish events plus the legacy whole-run
|
|
218
|
+
# +"complete"+ event. Ignored when +on_item_finish+ is also provided.
|
|
219
|
+
# @param on_item_start [#call, nil] optional callback invoked when a worker
|
|
220
|
+
# begins processing each item, before replay setup and customer code run.
|
|
221
|
+
# Pair it with on_item_finish to distinguish queued work from in-flight work.
|
|
222
|
+
# A raising callback never crashes the run.
|
|
216
223
|
# @return [Hash] with :items, :test_run_id, :test_run_url
|
|
217
224
|
def run(client, receiver, method_name, trace_function_key:, limit: nil, trace_ids: nil, name: nil,
|
|
218
225
|
max_concurrency: 10, code_change_description: CODE_CHANGE_UNSET, code_change_files: CODE_CHANGE_UNSET, experiment_group_id: nil,
|
|
219
226
|
dataset_id: nil, grader_ids: nil, mock: "marked",
|
|
220
|
-
adapt_inputs: nil, mock_override: nil, db_branch: nil, on_progress: nil)
|
|
227
|
+
adapt_inputs: nil, mock_override: nil, db_branch: nil, on_item_start: nil, on_item_finish: nil, on_progress: nil)
|
|
221
228
|
unless MOCK_STRATEGIES.include?(mock.to_s)
|
|
222
229
|
raise ArgumentError, "Invalid mock strategy '#{mock}'. Must be one of: #{MOCK_STRATEGIES.join(", ")}"
|
|
223
230
|
end
|
|
231
|
+
item_finish_callback = on_item_finish || on_progress
|
|
224
232
|
if trace_ids
|
|
225
233
|
raise ArgumentError, "trace_ids must contain at least one trace ID." if trace_ids.empty?
|
|
226
234
|
if trace_ids.length > 100
|
|
@@ -296,7 +304,8 @@ module Bitfab
|
|
|
296
304
|
|
|
297
305
|
result_items = if server_items.any?
|
|
298
306
|
process_items(http_client, server_items, receiver, method_name, test_run_id, max_concurrency, mock.to_s,
|
|
299
|
-
adapt_inputs, include_db_branch_lease, resolved_db_branch_settings,
|
|
307
|
+
adapt_inputs, include_db_branch_lease, resolved_db_branch_settings, on_item_start:,
|
|
308
|
+
on_item_finish: item_finish_callback,
|
|
300
309
|
mock_overrides: resolved_overrides)
|
|
301
310
|
else
|
|
302
311
|
[]
|
|
@@ -383,14 +392,12 @@ module Bitfab
|
|
|
383
392
|
test_run_id:,
|
|
384
393
|
test_run_url: full_test_run_url
|
|
385
394
|
}
|
|
386
|
-
# Persist the enriched result
|
|
387
|
-
#
|
|
388
|
-
# write it to BITFAB_REPLAY_RESULT_PATH when the plugin set that env var,
|
|
389
|
-
# and stream a terminal "complete" progress event. The plugin prefers the
|
|
390
|
-
# streamed event and falls back to the file. The event routes through
|
|
391
|
-
# on_progress so only progress-reporting runs emit it.
|
|
395
|
+
# Persist the enriched result so the Bitfab plugin never has to parse the
|
|
396
|
+
# replay's stdout, which a dependency's logging can corrupt.
|
|
392
397
|
write_replay_result_file(result)
|
|
393
|
-
|
|
398
|
+
# Preserve the legacy terminal event only for on_progress. on_item_finish
|
|
399
|
+
# is strictly item-scoped, so every invocation always includes an item.
|
|
400
|
+
if on_item_finish.nil? && on_progress
|
|
394
401
|
errored = result_items.count { |item| !item[:error].nil? }
|
|
395
402
|
total = result_items.length
|
|
396
403
|
begin
|
|
@@ -631,36 +638,56 @@ module Bitfab
|
|
|
631
638
|
|
|
632
639
|
# Process all replay items, optionally in parallel using threads.
|
|
633
640
|
def process_items(http_client, server_items, receiver, method_name, test_run_id, max_concurrency, mock_strategy,
|
|
634
|
-
adapt_inputs = nil, include_db_branch_lease = false, db_branch_settings = nil,
|
|
641
|
+
adapt_inputs = nil, include_db_branch_lease = false, db_branch_settings = nil, on_item_start: nil, on_item_finish: nil,
|
|
635
642
|
mock_overrides: [])
|
|
636
643
|
concurrency = max_concurrency || server_items.length
|
|
637
644
|
|
|
638
|
-
#
|
|
639
|
-
#
|
|
640
|
-
#
|
|
641
|
-
# concurrently). A raising callback is swallowed: progress UI must never
|
|
642
|
-
# crash the run.
|
|
645
|
+
# Lifecycle callbacks run from worker threads in the parallel path, so the
|
|
646
|
+
# mutex makes counter updates safe and serializes each callback. A raising
|
|
647
|
+
# callback is swallowed because progress UI must never crash the run.
|
|
643
648
|
total = server_items.length
|
|
644
649
|
progress_mutex = Mutex.new
|
|
650
|
+
started = 0
|
|
645
651
|
completed = 0
|
|
646
652
|
succeeded = 0
|
|
647
653
|
errored = 0
|
|
648
|
-
# Each event carries the single item that just
|
|
654
|
+
# Each event carries the single item that just finished so a progress UI
|
|
649
655
|
# can render per-trace pass/fail as the run streams. The item's :trace_id
|
|
650
656
|
# is nil at this stage: the server replay trace id isn't known until run()
|
|
651
657
|
# writes it in after complete_replay, and the client correlation id is
|
|
652
658
|
# never surfaced. original_trace_id (the historical trace being replayed,
|
|
653
659
|
# taken from the server item) is what a UI keys on to identify what just
|
|
654
|
-
#
|
|
655
|
-
|
|
656
|
-
|
|
660
|
+
# finished. source_trace_id/source_span_id are kept as deprecated aliases.
|
|
661
|
+
report_start = lambda do |original_trace_id, original_span_id|
|
|
662
|
+
progress_mutex.synchronize do
|
|
663
|
+
started += 1
|
|
664
|
+
next unless on_item_start
|
|
665
|
+
|
|
666
|
+
begin
|
|
667
|
+
on_item_start.call({
|
|
668
|
+
type: "started", test_run_id:, started:, completed:, total:, succeeded:, errored:,
|
|
669
|
+
item: {
|
|
670
|
+
original_trace_id:,
|
|
671
|
+
original_span_id:,
|
|
672
|
+
source_trace_id: original_trace_id,
|
|
673
|
+
source_span_id: original_span_id
|
|
674
|
+
}
|
|
675
|
+
})
|
|
676
|
+
rescue => e
|
|
677
|
+
warn "Bitfab: replay on_item_start callback raised: #{e.message}"
|
|
678
|
+
end
|
|
679
|
+
end
|
|
680
|
+
end
|
|
657
681
|
|
|
682
|
+
report = lambda do |result, original_trace_id, original_span_id, test_run_id|
|
|
658
683
|
progress_mutex.synchronize do
|
|
659
684
|
completed += 1
|
|
660
685
|
error = result[:error]
|
|
661
686
|
error.nil? ? (succeeded += 1) : (errored += 1)
|
|
687
|
+
next unless on_item_finish
|
|
688
|
+
|
|
662
689
|
begin
|
|
663
|
-
|
|
690
|
+
on_item_finish.call({
|
|
664
691
|
test_run_id:, completed:, total:, succeeded:, errored:,
|
|
665
692
|
item: {
|
|
666
693
|
trace_id: result[:trace_id],
|
|
@@ -682,13 +709,14 @@ module Bitfab
|
|
|
682
709
|
}
|
|
683
710
|
})
|
|
684
711
|
rescue => e
|
|
685
|
-
warn "Bitfab: replay
|
|
712
|
+
warn "Bitfab: replay on_item_finish callback raised: #{e.message}"
|
|
686
713
|
end
|
|
687
714
|
end
|
|
688
715
|
end
|
|
689
716
|
|
|
690
717
|
if concurrency <= 1
|
|
691
718
|
server_items.map do |item|
|
|
719
|
+
report_start.call(original_trace_id_of(item), original_span_id_of(item))
|
|
692
720
|
result = process_single_item(http_client, item, receiver, method_name, test_run_id, mock_strategy,
|
|
693
721
|
adapt_inputs, include_db_branch_lease, db_branch_settings, mock_overrides:)
|
|
694
722
|
report.call(result, original_trace_id_of(item), original_span_id_of(item), test_run_id)
|
|
@@ -706,6 +734,7 @@ module Bitfab
|
|
|
706
734
|
item, idx = work_mutex.synchronize { work_queue.shift }
|
|
707
735
|
break unless item
|
|
708
736
|
|
|
737
|
+
report_start.call(original_trace_id_of(item), original_span_id_of(item))
|
|
709
738
|
result = process_single_item(http_client, item, receiver, method_name, test_run_id, mock_strategy,
|
|
710
739
|
adapt_inputs, include_db_branch_lease, db_branch_settings, mock_overrides:)
|
|
711
740
|
results_mutex.synchronize { results[idx] = result }
|
data/lib/bitfab/version.rb
CHANGED
data/lib/bitfab.rb
CHANGED
|
@@ -144,20 +144,18 @@ module Bitfab
|
|
|
144
144
|
CurrentTrace.new(entry[:trace_id])
|
|
145
145
|
end
|
|
146
146
|
|
|
147
|
-
# A ready-made
|
|
148
|
-
#
|
|
149
|
-
# progress
|
|
150
|
-
#
|
|
151
|
-
# (progress must not crash a run). The progress hash's item is forwarded
|
|
152
|
-
# verbatim through to_json, so the plugin sees the source trace id, local
|
|
153
|
-
# replay trace id, outputs, error, and duration of the item that just settled.
|
|
147
|
+
# A ready-made replay lifecycle reporter. Pass it as both on_item_start and
|
|
148
|
+
# on_item_finish so the Bitfab plugin sees in-flight and finished items. It writes
|
|
149
|
+
# @@bitfab:progress lines to stderr, leaving stdout for ReplayResult JSON, and
|
|
150
|
+
# never raises because progress reporting must not crash a run.
|
|
154
151
|
#
|
|
155
152
|
# @example
|
|
156
|
-
#
|
|
153
|
+
# reporter = Bitfab.method(:report_replay_progress)
|
|
154
|
+
# Bitfab.client.replay(..., on_item_start: reporter, on_item_finish: reporter)
|
|
157
155
|
#
|
|
158
156
|
# @param progress [Hash] running totals with keys completed, total,
|
|
159
157
|
# succeeded, errored, and item (the object replay already passes to
|
|
160
|
-
#
|
|
158
|
+
# on_item_finish)
|
|
161
159
|
def report_replay_progress(progress)
|
|
162
160
|
warn "#{BITFAB_PROGRESS_PREFIX}#{Replay.json_safe(progress).to_json}"
|
|
163
161
|
rescue
|