bitfab 0.17.0 → 0.18.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 +49 -15
- data/lib/bitfab/http_client.rb +42 -1
- data/lib/bitfab/replay.rb +22 -12
- data/lib/bitfab/replay_environment.rb +19 -3
- 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: 4401b0c7aa47516909e636557ab23f325fb0cbbd4ca8564f3d8864078e58f975
|
|
4
|
+
data.tar.gz: 2b11fbfee500e0e03ec7b4b61772d623e2c236479965441b659aafbed4a687a6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e720bb8dd79d572f4671b5f891cb1528dac2b610d2357d1f16ec5a7e04656fff0969f434e5e683be991d8745865bacbbe1e37993380e1a09d2cc9377e221eda5
|
|
7
|
+
data.tar.gz: c197da185d7966cd9861213c49f0db4e3246a5eab0bdb70e800d8a4a5a4e8f174b5294d833511232555d43f0ca026a7988297fa3660743a8fef94b05b17a7ca9
|
data/lib/bitfab/client.rb
CHANGED
|
@@ -26,7 +26,7 @@ module Bitfab
|
|
|
26
26
|
@service_url = service_url || DEFAULT_SERVICE_URL
|
|
27
27
|
@enabled = enabled
|
|
28
28
|
if @enabled && (@api_key.nil? || @api_key.to_s.strip.empty?)
|
|
29
|
-
warn "Bitfab: api_key is empty
|
|
29
|
+
warn "Bitfab: api_key is empty: tracing is disabled. Provide a valid API key to enable tracing."
|
|
30
30
|
@enabled = false
|
|
31
31
|
end
|
|
32
32
|
@http_client = HttpClient.new(api_key:, service_url: @service_url)
|
|
@@ -87,7 +87,7 @@ module Bitfab
|
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
# Execute a block inside a span context, sending trace data on completion.
|
|
90
|
-
# Called by Traceable
|
|
90
|
+
# Called by Traceable, not intended for direct use.
|
|
91
91
|
def execute_span(trace_function_key:, span_name:, span_type:, function_name:, args:, kwargs:,
|
|
92
92
|
mock_on_replay: false)
|
|
93
93
|
return yield unless @enabled
|
|
@@ -121,7 +121,7 @@ module Bitfab
|
|
|
121
121
|
# Unmarked spans must consume an index so subsequent marked siblings
|
|
122
122
|
# line up with `build_mock_tree`'s sequential numbering for the same
|
|
123
123
|
# (key, name) pair. Different (key, name) pairs have independent
|
|
124
|
-
# counters
|
|
124
|
+
# counters: they cannot shift each other.
|
|
125
125
|
call_index = advance_mock_counter(replay_ctx, trace_function_key, span_name, is_root_span:)
|
|
126
126
|
if call_index
|
|
127
127
|
mocked_output = check_mock_replay(
|
|
@@ -154,7 +154,7 @@ module Bitfab
|
|
|
154
154
|
finalized = false
|
|
155
155
|
|
|
156
156
|
finalize = lambda do |final_result, final_error|
|
|
157
|
-
# Never crash the host app due to span building/sending. Idempotent
|
|
157
|
+
# Never crash the host app due to span building/sending. Idempotent:
|
|
158
158
|
# only the first call sends the span. Subsequent calls (e.g. from the
|
|
159
159
|
# enumerator wrapper after iteration completes) are no-ops.
|
|
160
160
|
next if finalized
|
|
@@ -188,11 +188,27 @@ module Bitfab
|
|
|
188
188
|
pending << span_thread if span_thread
|
|
189
189
|
pending.each { |t| t.join(5) }
|
|
190
190
|
|
|
191
|
+
# Built AFTER the wrapped method finished (finalize runs at root
|
|
192
|
+
# span end), so :accessed reflects whether customer code obtained
|
|
193
|
+
# the branch URL during this item. nil (key omitted) when no
|
|
194
|
+
# lease was attached, so the server can distinguish "no branch"
|
|
195
|
+
# from "branch ignored".
|
|
196
|
+
lease = replay_ctx&.dig(:db_branch_lease)
|
|
197
|
+
db_snapshot_usage = if lease
|
|
198
|
+
{
|
|
199
|
+
neon_branch_id: lease["neonBranchId"],
|
|
200
|
+
snapshot_timestamp: lease["snapshotTimestamp"],
|
|
201
|
+
source_trace_id: replay_ctx[:source_bitfab_trace_id],
|
|
202
|
+
accessed: replay_ctx[:db_snapshot_accessed] == true
|
|
203
|
+
}
|
|
204
|
+
end
|
|
205
|
+
|
|
191
206
|
completion_thread = send_trace_completion(
|
|
192
207
|
trace_function_key:,
|
|
193
208
|
trace_id:,
|
|
194
209
|
started_at:,
|
|
195
|
-
ended_at
|
|
210
|
+
ended_at:,
|
|
211
|
+
db_snapshot_usage:
|
|
196
212
|
)
|
|
197
213
|
|
|
198
214
|
# In replay, persistence is correctness: the replay runner joins
|
|
@@ -212,7 +228,7 @@ module Bitfab
|
|
|
212
228
|
end
|
|
213
229
|
end
|
|
214
230
|
rescue Exception # rubocop:disable Lint/RescueException
|
|
215
|
-
# Silently ignore
|
|
231
|
+
# Silently ignore: user's result/exception takes priority
|
|
216
232
|
# Catches Exception (not just StandardError) to handle SystemStackError
|
|
217
233
|
# from deeply nested serialization
|
|
218
234
|
end
|
|
@@ -234,7 +250,7 @@ module Bitfab
|
|
|
234
250
|
|
|
235
251
|
# If the wrapped block returned an Enumerator (lazy iteration via
|
|
236
252
|
# `enum_for`, `to_enum`, `Enumerator.new`, `[...].lazy.map(...)`, etc.),
|
|
237
|
-
# the work hasn't actually run yet
|
|
253
|
+
# the work hasn't actually run yet: the values are produced as the
|
|
238
254
|
# caller iterates. Without special handling we'd close the span here
|
|
239
255
|
# with `result == <the Enumerator object>`, and any nested `bitfab_span`
|
|
240
256
|
# calls inside the enumerator body would see an empty span stack and
|
|
@@ -264,7 +280,7 @@ module Bitfab
|
|
|
264
280
|
# Build an Enumerator that drives `source`, restoring `[trace_id, span_id]`
|
|
265
281
|
# on the iterating fiber so nested `bitfab_span` calls inside lazy / `each`
|
|
266
282
|
# callbacks nest under the parent span. Yielded values are collected as the
|
|
267
|
-
# span output. The span is sent exactly once
|
|
283
|
+
# span output. The span is sent exactly once: when iteration finishes,
|
|
268
284
|
# raises, or the wrapper is `.close`d.
|
|
269
285
|
def wrap_enumerator(source, trace_id:, span_id:, finalize:)
|
|
270
286
|
span_entry = {trace_id:, span_id:}
|
|
@@ -296,7 +312,14 @@ module Bitfab
|
|
|
296
312
|
raise ArgumentError, "Invalid span type '#{type}'. Must be one of: #{SPAN_TYPES.join(", ")}"
|
|
297
313
|
end
|
|
298
314
|
|
|
299
|
-
|
|
315
|
+
# db_snapshot_usage: replay DB branch usage record, present only when a
|
|
316
|
+
# lease was attached to the replay item. Serialized as `db_snapshot_usage`
|
|
317
|
+
# on the raw trace so the server can stamp the trace's metadata at ingest:
|
|
318
|
+
# { neon_branch_id:, snapshot_timestamp: (optional), source_trace_id:
|
|
319
|
+
# (optional), accessed: } with :accessed true if customer code obtained
|
|
320
|
+
# the branch URL and false if it ignored it. nil outside replay or when no
|
|
321
|
+
# lease was attached, in which case the key is omitted entirely.
|
|
322
|
+
def send_trace_completion(trace_function_key:, trace_id:, started_at:, ended_at:, db_snapshot_usage: nil)
|
|
300
323
|
trace_state = TraceState.get(trace_id)
|
|
301
324
|
trace_started_at = trace_state&.dig(:started_at) || started_at
|
|
302
325
|
|
|
@@ -318,6 +341,17 @@ module Bitfab
|
|
|
318
341
|
if trace_state&.dig(:db_snapshot_ref)
|
|
319
342
|
raw_trace["db_snapshot_ref"] = trace_state[:db_snapshot_ref]
|
|
320
343
|
end
|
|
344
|
+
if db_snapshot_usage
|
|
345
|
+
usage = {"neon_branch_id" => db_snapshot_usage[:neon_branch_id]}
|
|
346
|
+
if db_snapshot_usage[:snapshot_timestamp]
|
|
347
|
+
usage["snapshot_timestamp"] = db_snapshot_usage[:snapshot_timestamp]
|
|
348
|
+
end
|
|
349
|
+
if db_snapshot_usage[:source_trace_id]
|
|
350
|
+
usage["source_trace_id"] = db_snapshot_usage[:source_trace_id]
|
|
351
|
+
end
|
|
352
|
+
usage["accessed"] = db_snapshot_usage[:accessed]
|
|
353
|
+
raw_trace["db_snapshot_usage"] = usage
|
|
354
|
+
end
|
|
321
355
|
|
|
322
356
|
payload = {
|
|
323
357
|
"type" => "sdk-function",
|
|
@@ -339,7 +373,7 @@ module Bitfab
|
|
|
339
373
|
# Clean up trace state
|
|
340
374
|
TraceState.delete(trace_id)
|
|
341
375
|
|
|
342
|
-
# Returned so the replay path can join it
|
|
376
|
+
# Returned so the replay path can join it: trace completions must be
|
|
343
377
|
# persisted before complete_replay builds the trace-ID mapping.
|
|
344
378
|
completion_thread
|
|
345
379
|
end
|
|
@@ -398,8 +432,8 @@ module Bitfab
|
|
|
398
432
|
# non-root span under an active mock tree. Returns the call index this
|
|
399
433
|
# invocation owns, or nil when there's nothing to advance (root span, or
|
|
400
434
|
# no replay mock context). The counter MUST advance for every child span
|
|
401
|
-
# sharing the same (key, name) pair
|
|
402
|
-
# mocked
|
|
435
|
+
# sharing the same (key, name) pair (including spans that won't be
|
|
436
|
+
# mocked) so unmarked spans don't silently shift subsequent marked
|
|
403
437
|
# spans' indices. Different (key, name) pairs have independent counters.
|
|
404
438
|
def advance_mock_counter(replay_ctx, trace_function_key, span_name, is_root_span:)
|
|
405
439
|
return nil if is_root_span
|
|
@@ -433,7 +467,7 @@ module Bitfab
|
|
|
433
467
|
output_meta = mock_entry[:output_meta]
|
|
434
468
|
|
|
435
469
|
# Type-preserving deserialization when the server included Ruby-side
|
|
436
|
-
# Marshal+Base64 metadata. Falls back to the JSON output silently
|
|
470
|
+
# Marshal+Base64 metadata. Falls back to the JSON output silently: the
|
|
437
471
|
# spanTree endpoint currently returns superjson/jsonpickle-shaped meta,
|
|
438
472
|
# which Ruby cannot reconstruct.
|
|
439
473
|
if output_meta.is_a?(String) && !output_meta.empty?
|
|
@@ -484,13 +518,13 @@ module Bitfab
|
|
|
484
518
|
@pending_span_threads[trace_id] << span_thread if span_thread && @pending_span_threads.key?(trace_id)
|
|
485
519
|
end
|
|
486
520
|
rescue Exception # rubocop:disable Lint/RescueException
|
|
487
|
-
# Never crash the host app
|
|
521
|
+
# Never crash the host app: mocked span recording is best-effort
|
|
488
522
|
end
|
|
489
523
|
end
|
|
490
524
|
|
|
491
525
|
# Fluent wrapper bound to a single trace_function_key. Mirrors
|
|
492
526
|
# `BitfabFunction` in the Python SDK and `BitfabFunction` in the TypeScript
|
|
493
|
-
# SDK
|
|
527
|
+
# SDK: lets callers wrap multiple methods without repeating the key.
|
|
494
528
|
class BitfabFunction
|
|
495
529
|
attr_reader :trace_function_key
|
|
496
530
|
|
data/lib/bitfab/http_client.rb
CHANGED
|
@@ -5,6 +5,7 @@ require "json"
|
|
|
5
5
|
require "uri"
|
|
6
6
|
|
|
7
7
|
require_relative "constants"
|
|
8
|
+
require_relative "serialize"
|
|
8
9
|
require_relative "version"
|
|
9
10
|
|
|
10
11
|
module Bitfab
|
|
@@ -32,7 +33,7 @@ module Bitfab
|
|
|
32
33
|
http.read_timeout = request_timeout
|
|
33
34
|
|
|
34
35
|
req = Net::HTTP::Post.new(uri.path, headers)
|
|
35
|
-
req.body =
|
|
36
|
+
req.body = safe_generate(payload, endpoint)
|
|
36
37
|
|
|
37
38
|
response = http.request(req)
|
|
38
39
|
|
|
@@ -166,6 +167,46 @@ module Bitfab
|
|
|
166
167
|
|
|
167
168
|
private
|
|
168
169
|
|
|
170
|
+
# JSON-encode a request body without ever raising on a stray value.
|
|
171
|
+
#
|
|
172
|
+
# Upstream serialization (Serialize.serialize_value) should already have
|
|
173
|
+
# flattened user data. This is the boundary backstop: if anything
|
|
174
|
+
# non-serializable still slips through, it is run through serialize_value
|
|
175
|
+
# (which never raises and stubs strays) instead of letting JSON.generate
|
|
176
|
+
# raise and drop the whole span/trace silently. A degraded payload warns
|
|
177
|
+
# loudly so the trace isn't quietly left incomplete or not replayable.
|
|
178
|
+
def safe_generate(payload, endpoint)
|
|
179
|
+
JSON.generate(payload)
|
|
180
|
+
rescue => e
|
|
181
|
+
begin
|
|
182
|
+
warn "Bitfab: request body to #{endpoint} held a non-serializable " \
|
|
183
|
+
"value (#{e.message}); it was stubbed so the span still sends, " \
|
|
184
|
+
"but the trace may be incomplete or not replayable. Capture a " \
|
|
185
|
+
"JSON-safe projection of this input to make it replayable."
|
|
186
|
+
rescue
|
|
187
|
+
# Never crash the host app over a log line.
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
begin
|
|
191
|
+
JSON.generate(sanitize_payload(payload))
|
|
192
|
+
rescue
|
|
193
|
+
# Truly pathological. Still never drop silently: send a marker body.
|
|
194
|
+
JSON.generate({"error" => "payload_serialize_failed"})
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Serialize each top-level value so a bad or oversize value is stubbed in
|
|
199
|
+
# place while the payload keeps its object shape. Running serialize_value on
|
|
200
|
+
# the whole payload could collapse the entire body to a single stub string
|
|
201
|
+
# (oversize/cyclic), sending a JSON string instead of a span object.
|
|
202
|
+
def sanitize_payload(payload)
|
|
203
|
+
return {"error" => "payload_serialize_failed"} unless payload.is_a?(Hash)
|
|
204
|
+
|
|
205
|
+
payload.each_with_object({}) do |(k, v), acc|
|
|
206
|
+
acc[k.to_s] = Serialize.serialize_value(v)
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
169
210
|
# Normalize each entry to a hash with stable string keys, accepting either
|
|
170
211
|
# symbol-keyed or string-keyed hashes from callers.
|
|
171
212
|
def normalize_code_change_files(files)
|
data/lib/bitfab/replay.rb
CHANGED
|
@@ -6,9 +6,9 @@ require_relative "serialize"
|
|
|
6
6
|
module Bitfab
|
|
7
7
|
# Replay mock strategies. Mirrors the Python and TypeScript SDKs.
|
|
8
8
|
#
|
|
9
|
-
# - "none"
|
|
10
|
-
# - "all"
|
|
11
|
-
# - "marked"
|
|
9
|
+
# - "none" : every child span runs real code (default)
|
|
10
|
+
# - "all" : every child span returns its historical output
|
|
11
|
+
# - "marked" : only spans declared with mock_on_replay: true return historical
|
|
12
12
|
# output; everything else runs real code
|
|
13
13
|
MOCK_STRATEGIES = %w[none all marked].freeze
|
|
14
14
|
|
|
@@ -45,6 +45,16 @@ module Bitfab
|
|
|
45
45
|
# The per-trace DB branch (resolved server-side) and the Bitfab trace ID
|
|
46
46
|
# it belongs to ride on the context so ReplayEnvironment can read them
|
|
47
47
|
# inside the replayed method.
|
|
48
|
+
#
|
|
49
|
+
# ReplayEnvironment also sets ctx[:db_snapshot_accessed] = true the
|
|
50
|
+
# first time customer code actually obtains the branch URL for this
|
|
51
|
+
# item (via +database_url+ or +snapshot+). Reported on the trace
|
|
52
|
+
# completion inside the +db_snapshot_usage+ record (its +accessed+
|
|
53
|
+
# field) so the server can distinguish "branch was provisioned and
|
|
54
|
+
# exposed" from "branch URL was actually consumed". Any future
|
|
55
|
+
# consumption path that hands the URL to customer code by other means
|
|
56
|
+
# (e.g. a process-isolated runner writing an env overlay) must also
|
|
57
|
+
# set this.
|
|
48
58
|
ctx[:db_branch_lease] = db_branch_lease if db_branch_lease
|
|
49
59
|
ctx[:source_bitfab_trace_id] = source_bitfab_trace_id if source_bitfab_trace_id
|
|
50
60
|
Thread.current[REPLAY_CONTEXT_KEY] = ctx
|
|
@@ -135,7 +145,7 @@ module Bitfab
|
|
|
135
145
|
|
|
136
146
|
# Every item joined its own trace-persistence threads (span uploads +
|
|
137
147
|
# completion) in execute_item, so all replay traces are on the server
|
|
138
|
-
# by now
|
|
148
|
+
# by now: no flush needed, and complete_replay's trace-ID mapping is
|
|
139
149
|
# deterministic. complete_replay failures propagate: a missing mapping
|
|
140
150
|
# means verdicts can't be persisted, which callers must hear about
|
|
141
151
|
# loudly.
|
|
@@ -151,7 +161,7 @@ module Bitfab
|
|
|
151
161
|
else
|
|
152
162
|
# Map each item's locally-generated trace ID to the server's trace
|
|
153
163
|
# row ID. A completed item with no mapping means its trace was sent
|
|
154
|
-
# but the server has no record
|
|
164
|
+
# but the server has no record: a nil trace_id blocks verdict
|
|
155
165
|
# persistence and the Studio experiments view downstream, so this
|
|
156
166
|
# must never be silent.
|
|
157
167
|
#
|
|
@@ -244,8 +254,8 @@ module Bitfab
|
|
|
244
254
|
metrics = extract_server_item_metrics(server_item)
|
|
245
255
|
# The server resolves a Neon preview branch per item during /replay/start
|
|
246
256
|
# (only when include_db_branch_lease was sent). Release it in the +ensure+
|
|
247
|
-
# below so any raise
|
|
248
|
-
# method
|
|
257
|
+
# below so any raise (span fetch, mock-tree build, or the replayed
|
|
258
|
+
# method) frees the Neon resource. Items whose source trace had no
|
|
249
259
|
# snapshot ref, or whose resolve failed server-side, arrive without a
|
|
250
260
|
# lease (env.active? is false for those).
|
|
251
261
|
lease = include_db_branch_lease ? server_item["dbBranchLease"] : nil
|
|
@@ -295,7 +305,7 @@ module Bitfab
|
|
|
295
305
|
end
|
|
296
306
|
|
|
297
307
|
# Delete the per-item Neon preview branch. Best-effort: a failure is warned
|
|
298
|
-
# but never raised
|
|
308
|
+
# but never raised: the server-side TTL janitor reaps orphans.
|
|
299
309
|
def release_db_branch_lease(http_client, lease)
|
|
300
310
|
neon_branch_id = lease["neonBranchId"]
|
|
301
311
|
return unless neon_branch_id
|
|
@@ -308,7 +318,7 @@ module Bitfab
|
|
|
308
318
|
# Walk the children of a root span tree node depth-first and build a
|
|
309
319
|
# lookup keyed by "#{trace_function_key}:#{span_name}:#{call_index}".
|
|
310
320
|
#
|
|
311
|
-
# The root node itself is excluded
|
|
321
|
+
# The root node itself is excluded: at replay time the runtime root span
|
|
312
322
|
# never queries the mock tree.
|
|
313
323
|
#
|
|
314
324
|
# The compound (key, name) match disambiguates same-key spans that come
|
|
@@ -316,7 +326,7 @@ module Bitfab
|
|
|
316
326
|
# wrapped method shares trace_function_key but differs in span_name. The
|
|
317
327
|
# counter is per-(key, name) pair so repeated same-name calls (including
|
|
318
328
|
# recursion) still order by occurrence. Mirrors the Python and TypeScript
|
|
319
|
-
# SDKs after HVT-2078
|
|
329
|
+
# SDKs after HVT-2078: keying by trace_function_key alone caused the
|
|
320
330
|
# wrong historical output for fluent-API span sets.
|
|
321
331
|
def build_mock_tree(root)
|
|
322
332
|
spans = {}
|
|
@@ -389,7 +399,7 @@ module Bitfab
|
|
|
389
399
|
sdk_trace_id = SecureRandom.uuid
|
|
390
400
|
# Collects the root span's persistence threads (span uploads + trace
|
|
391
401
|
# completion). Joined below so this item's trace is on the server
|
|
392
|
-
# before run() calls complete_replay
|
|
402
|
+
# before run() calls complete_replay: otherwise the server's trace-ID
|
|
393
403
|
# mapping races the uploads and the item's trace_id comes back nil.
|
|
394
404
|
pending_persistence = []
|
|
395
405
|
|
|
@@ -421,7 +431,7 @@ module Bitfab
|
|
|
421
431
|
end
|
|
422
432
|
|
|
423
433
|
# Wait for this item's trace (spans + completion) to be fully persisted
|
|
424
|
-
# before the item resolves. Runs on the error path too
|
|
434
|
+
# before the item resolves. Runs on the error path too: a raising
|
|
425
435
|
# method still emits a root span whose trace must land before
|
|
426
436
|
# complete_replay. Joins are bounded by the HTTP layer's own timeouts.
|
|
427
437
|
pending_persistence.each(&:join)
|
|
@@ -23,7 +23,9 @@ module Bitfab
|
|
|
23
23
|
# The per-trace branch URL for the item currently being replayed.
|
|
24
24
|
# Raises if read outside a replay item.
|
|
25
25
|
def database_url
|
|
26
|
-
require_snapshot
|
|
26
|
+
snap = require_snapshot
|
|
27
|
+
mark_accessed
|
|
28
|
+
snap.fetch(:database_url)
|
|
27
29
|
end
|
|
28
30
|
|
|
29
31
|
# When the per-trace branch URL stops being valid. ISO-8601.
|
|
@@ -55,11 +57,25 @@ module Bitfab
|
|
|
55
57
|
# Non-raising variant for callers that handle the inactive case. Returns a
|
|
56
58
|
# symbol-keyed hash or nil.
|
|
57
59
|
def snapshot
|
|
58
|
-
read_snapshot
|
|
60
|
+
snap = read_snapshot
|
|
61
|
+
mark_accessed if snap
|
|
62
|
+
snap
|
|
59
63
|
end
|
|
60
64
|
|
|
61
65
|
private
|
|
62
66
|
|
|
67
|
+
# Record on the replay context that customer code obtained the branch
|
|
68
|
+
# URL. Only +database_url+ and +snapshot+ count: +active?+, +read_only+
|
|
69
|
+
# and friends inspect the lease without exposing the connection string,
|
|
70
|
+
# so they don't prove the replayed code could have connected to the
|
|
71
|
+
# branch.
|
|
72
|
+
def mark_accessed
|
|
73
|
+
ctx = ReplayContext.current
|
|
74
|
+
return unless ctx && ctx[:db_branch_lease]
|
|
75
|
+
|
|
76
|
+
ctx[:db_snapshot_accessed] = true
|
|
77
|
+
end
|
|
78
|
+
|
|
63
79
|
def read_snapshot
|
|
64
80
|
ctx = ReplayContext.current
|
|
65
81
|
return nil unless ctx
|
|
@@ -69,7 +85,7 @@ module Bitfab
|
|
|
69
85
|
|
|
70
86
|
# Surface the Bitfab trace ID (what the customer sees in the dashboard),
|
|
71
87
|
# falling back to the external trace ID only if the Bitfab ID is somehow
|
|
72
|
-
# absent
|
|
88
|
+
# absent: keeps replays from external sources working until the
|
|
73
89
|
# source-system path is fully wired.
|
|
74
90
|
trace_id = ctx[:source_bitfab_trace_id] || ctx[:input_source_trace_id]
|
|
75
91
|
return nil unless trace_id
|
data/lib/bitfab/version.rb
CHANGED