bitfab 0.32.0 → 0.33.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 +7 -2
- data/lib/bitfab/replay.rb +16 -11
- data/lib/bitfab/replay_branch.rb +68 -0
- data/lib/bitfab/version.rb +1 -1
- data/lib/bitfab.rb +29 -1
- metadata +2 -2
- data/lib/bitfab/replay_environment.rb +0 -148
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fa66ab7393ccfdd162cdeaecee49c7b18efa601244afc8d7830568602968efd2
|
|
4
|
+
data.tar.gz: 29312e2278ad9a39033f82f01a45c20ef12bee1f3ac4b6140c94b22f0ae9dc48
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dda0002685836ac6e381ecbf37430950df3fa7df9bad695e80803bb1c0da432885027be571feb02ba571adbc4c98a927e1a67eb910f3c8784c0bfde18e4b4f75
|
|
7
|
+
data.tar.gz: 183c49cc2d9ed286d3ea94e5505e09d6efdfe1cd2e5d73c31e1d504029643745bb796029395c70dc8afc1b2f165b3ecb84d2798933b806d0c00bd8b483d7a94e
|
data/lib/bitfab/client.rb
CHANGED
|
@@ -105,6 +105,11 @@ module Bitfab
|
|
|
105
105
|
# first matching override wins. Per-call overrides take precedence over
|
|
106
106
|
# those registered via register_mock_override, and both take precedence
|
|
107
107
|
# over the base mock strategy. The root span is never overridden.
|
|
108
|
+
# @param db_branch [Hash, nil] passing it at all turns database branching
|
|
109
|
+
# on, so +{}+ enables it with the mirror project's own sizing. Keys
|
|
110
|
+
# +:min_cu+/+:max_cu+ size the branch compute in Neon Compute Units and
|
|
111
|
+
# +:warmup_sql+ warms its cache. Read the resolved branch inside the
|
|
112
|
+
# replayed method with +Bitfab.current_replay_branch+.
|
|
108
113
|
# @param on_progress [#call, nil] optional callback invoked once per item as
|
|
109
114
|
# it finishes, with a running-totals hash { completed:, total:, succeeded:,
|
|
110
115
|
# errored: }. Use it to render replay progress (e.g. a terminal progress
|
|
@@ -114,12 +119,12 @@ module Bitfab
|
|
|
114
119
|
# @return [Hash] with :items, :test_run_id, :test_run_url
|
|
115
120
|
def replay(receiver, method_name, trace_function_key:, limit: nil, trace_ids: nil, max_concurrency: 10,
|
|
116
121
|
name: nil, code_change_description: nil, code_change_files: nil, experiment_group_id: nil, dataset_id: nil, grader_ids: nil, mock: "marked",
|
|
117
|
-
adapt_inputs: nil, mock_override: nil,
|
|
122
|
+
adapt_inputs: nil, mock_override: nil, db_branch: nil, on_progress: nil)
|
|
118
123
|
Replay.run(
|
|
119
124
|
self, receiver, method_name,
|
|
120
125
|
trace_function_key:, limit:, trace_ids:, name:, max_concurrency:,
|
|
121
126
|
code_change_description:, code_change_files:, experiment_group_id:, dataset_id:, grader_ids:, mock:, adapt_inputs:,
|
|
122
|
-
mock_override:,
|
|
127
|
+
mock_override:, db_branch:,
|
|
123
128
|
on_progress:
|
|
124
129
|
)
|
|
125
130
|
end
|
data/lib/bitfab/replay.rb
CHANGED
|
@@ -58,12 +58,12 @@ module Bitfab
|
|
|
58
58
|
ctx[:fetch_span_output] = fetch_span_output if fetch_span_output
|
|
59
59
|
end
|
|
60
60
|
# The per-trace DB branch (resolved server-side) and the Bitfab trace ID
|
|
61
|
-
# it belongs to ride on the context so
|
|
62
|
-
#
|
|
61
|
+
# it belongs to ride on the context so ReplayBranch can read them inside
|
|
62
|
+
# the replayed method.
|
|
63
63
|
#
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
#
|
|
64
|
+
# ReplayBranch also sets ctx[:db_snapshot_accessed] = true the first
|
|
65
|
+
# time customer code actually obtains the branch URL for this item
|
|
66
|
+
# (via +database_url+). Reported on the trace
|
|
67
67
|
# completion inside the +db_snapshot_usage+ record (its +accessed+
|
|
68
68
|
# field) so the server can distinguish "branch was provisioned and
|
|
69
69
|
# exposed" from "branch URL was actually consumed". Any future
|
|
@@ -120,6 +120,12 @@ module Bitfab
|
|
|
120
120
|
# source_trace_id/source_span_id aliases), and returns [new_args, new_kwargs].
|
|
121
121
|
# Runs per item inside the same rescue as the method, so a raising adapter
|
|
122
122
|
# sets that item's :error rather than crashing the run.
|
|
123
|
+
# @param db_branch [Hash, nil] passing it at all turns database branching
|
|
124
|
+
# on, so +{}+ enables it with the mirror project's own sizing. Keys
|
|
125
|
+
# +:min_cu+/+:max_cu+ are the branch compute's autoscaling floor and
|
|
126
|
+
# ceiling in Neon Compute Units, and +:warmup_sql+ warms the branch's
|
|
127
|
+
# cache before the replayed method sees it. Read the resolved branch
|
|
128
|
+
# inside the method with +Bitfab.current_replay_branch+.
|
|
123
129
|
# @param on_progress [#call, nil] optional callback invoked once per item as
|
|
124
130
|
# it finishes, with a running-totals hash { completed:, total:, succeeded:,
|
|
125
131
|
# errored:, item: } where item is { trace_id:, original_trace_id:,
|
|
@@ -136,7 +142,7 @@ module Bitfab
|
|
|
136
142
|
def run(client, receiver, method_name, trace_function_key:, limit: nil, trace_ids: nil, name: nil,
|
|
137
143
|
max_concurrency: 10, code_change_description: nil, code_change_files: nil, experiment_group_id: nil,
|
|
138
144
|
dataset_id: nil, grader_ids: nil, mock: "marked",
|
|
139
|
-
adapt_inputs: nil, mock_override: nil,
|
|
145
|
+
adapt_inputs: nil, mock_override: nil, db_branch: nil, on_progress: nil)
|
|
140
146
|
unless MOCK_STRATEGIES.include?(mock.to_s)
|
|
141
147
|
raise ArgumentError, "Invalid mock strategy '#{mock}'. Must be one of: #{MOCK_STRATEGIES.join(", ")}"
|
|
142
148
|
end
|
|
@@ -179,7 +185,7 @@ module Bitfab
|
|
|
179
185
|
# the count), so it's omitted from the request entirely.
|
|
180
186
|
effective_limit = trace_ids ? nil : (limit || 5)
|
|
181
187
|
|
|
182
|
-
include_db_branch_lease = !
|
|
188
|
+
include_db_branch_lease = !db_branch.nil?
|
|
183
189
|
|
|
184
190
|
# An explicit code change always wins; only when the caller passed neither
|
|
185
191
|
# field do we fall back to the payload the replay wrapper captured from git
|
|
@@ -205,7 +211,7 @@ module Bitfab
|
|
|
205
211
|
include_db_branch_lease:,
|
|
206
212
|
dataset_id:,
|
|
207
213
|
grader_ids:,
|
|
208
|
-
db_branch_settings: db_branch_settings(
|
|
214
|
+
db_branch_settings: db_branch_settings(db_branch)
|
|
209
215
|
)
|
|
210
216
|
test_run_id = replay_data["testRunId"]
|
|
211
217
|
test_run_url = replay_data["testRunUrl"]
|
|
@@ -320,7 +326,7 @@ module Bitfab
|
|
|
320
326
|
CC_MAX_FILE_BYTES = 500_000
|
|
321
327
|
CC_MAX_TOTAL_BYTES = 2_000_000
|
|
322
328
|
|
|
323
|
-
# Convert the
|
|
329
|
+
# Convert the caller's +db_branch+ options to the wire shape, or nil when
|
|
324
330
|
# nothing was set. Dropping the key entirely keeps the request identical to
|
|
325
331
|
# what older SDKs send.
|
|
326
332
|
#
|
|
@@ -328,8 +334,7 @@ module Bitfab
|
|
|
328
334
|
# ceiling in Neon Compute Units (equal values pin the size, keeping items
|
|
329
335
|
# comparable); :warmup_sql is appended to the branch's readiness check so it
|
|
330
336
|
# warms the cache before the replayed function sees the lease.
|
|
331
|
-
def db_branch_settings(
|
|
332
|
-
db_branch = environment&.db_branch
|
|
337
|
+
def db_branch_settings(db_branch)
|
|
333
338
|
return nil if db_branch.nil? || db_branch.empty?
|
|
334
339
|
|
|
335
340
|
# Symbol or string keys, matching normalize_code_change_files: a hash
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bitfab
|
|
4
|
+
# The database branch a single replay item runs against.
|
|
5
|
+
#
|
|
6
|
+
# +Bitfab.current_replay_branch+ hands you one inside a replayed method when
|
|
7
|
+
# the source trace carried a DB snapshot reference and the Bitfab service
|
|
8
|
+
# resolved a branch from it. Outside a replay item, or when no branch was
|
|
9
|
+
# resolved, that reader returns nil and your code keeps reading
|
|
10
|
+
# +ENV["DATABASE_URL"]+ the normal way.
|
|
11
|
+
#
|
|
12
|
+
# Immutable and scoped to one item: the reader builds it from the thread-local
|
|
13
|
+
# replay context, so parallel replay items each see their own branch and no
|
|
14
|
+
# lease state lives on a long-lived object.
|
|
15
|
+
#
|
|
16
|
+
# Internally the resolved per-item state is a DB branch lease (the SDK/server
|
|
17
|
+
# protocol term). Its useful fields are exposed directly here so customer code
|
|
18
|
+
# never sees the word.
|
|
19
|
+
class ReplayBranch
|
|
20
|
+
# When this branch's URL stops being valid. ISO-8601.
|
|
21
|
+
attr_reader :expires_at
|
|
22
|
+
|
|
23
|
+
# Deep link to the branch in the provider console, if available.
|
|
24
|
+
attr_reader :provider_console_url
|
|
25
|
+
|
|
26
|
+
# True if the branch is read-only. Use it to skip write operations during
|
|
27
|
+
# replay when the provider returned a read-only lease.
|
|
28
|
+
attr_reader :read_only
|
|
29
|
+
|
|
30
|
+
# The branch's region, e.g. "aws-us-east-1". A compute runs in its project's
|
|
31
|
+
# region, so a replay runner elsewhere pays that round trip on every query.
|
|
32
|
+
attr_reader :region
|
|
33
|
+
|
|
34
|
+
# The historical trace ID that produced the input for this replay item.
|
|
35
|
+
attr_reader :trace_id
|
|
36
|
+
|
|
37
|
+
# Built by Bitfab.current_replay_branch; never constructed by callers.
|
|
38
|
+
def initialize(lease, trace_id, context)
|
|
39
|
+
@expires_at = lease["expiresAt"]
|
|
40
|
+
@provider_console_url = lease["providerConsoleUrl"]
|
|
41
|
+
@read_only = lease["readOnly"]
|
|
42
|
+
@region = lease["region"]
|
|
43
|
+
@trace_id = trace_id
|
|
44
|
+
@url = lease["databaseUrl"]
|
|
45
|
+
@context = context
|
|
46
|
+
freeze
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Connection string for this item's branch. Point your database client at it
|
|
50
|
+
# instead of the live database for the duration of the replayed call.
|
|
51
|
+
#
|
|
52
|
+
# Reading it records on the trace that the replayed code obtained the branch
|
|
53
|
+
# URL, which is what separates "a branch was provisioned" from "the branch
|
|
54
|
+
# was actually used". The other readers inspect the lease without exposing
|
|
55
|
+
# the connection string, so they deliberately do not record anything.
|
|
56
|
+
def database_url
|
|
57
|
+
@context[:db_snapshot_accessed] = true
|
|
58
|
+
@url
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Redact the connection string so a logged or inspected branch cannot leak it.
|
|
62
|
+
def inspect
|
|
63
|
+
"#<Bitfab::ReplayBranch trace_id=#{@trace_id.inspect} " \
|
|
64
|
+
"expires_at=#{@expires_at.inspect} region=#{@region.inspect} " \
|
|
65
|
+
"read_only=#{@read_only.inspect}>"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
data/lib/bitfab/version.rb
CHANGED
data/lib/bitfab.rb
CHANGED
|
@@ -11,7 +11,7 @@ require_relative "bitfab/span_context"
|
|
|
11
11
|
require_relative "bitfab/http_client"
|
|
12
12
|
require_relative "bitfab/mock_override"
|
|
13
13
|
require_relative "bitfab/replay"
|
|
14
|
-
require_relative "bitfab/
|
|
14
|
+
require_relative "bitfab/replay_branch"
|
|
15
15
|
require_relative "bitfab/client"
|
|
16
16
|
require_relative "bitfab/traceable"
|
|
17
17
|
|
|
@@ -102,6 +102,34 @@ module Bitfab
|
|
|
102
102
|
CurrentSpan.new(entry)
|
|
103
103
|
end
|
|
104
104
|
|
|
105
|
+
# Get the database branch the current replay item is running against.
|
|
106
|
+
#
|
|
107
|
+
# Call this from inside a method being replayed with
|
|
108
|
+
# +client.replay(db_branch: ...)+ and point your database client at
|
|
109
|
+
# +branch.database_url+ so the replay reads the data as it was at trace
|
|
110
|
+
# time:
|
|
111
|
+
#
|
|
112
|
+
# branch = Bitfab.current_replay_branch
|
|
113
|
+
# url = branch ? branch.database_url : ENV["DATABASE_URL"]
|
|
114
|
+
#
|
|
115
|
+
# @return [ReplayBranch, nil] nil outside a replay item, and for an item
|
|
116
|
+
# whose source trace carried no DB snapshot reference
|
|
117
|
+
def current_replay_branch
|
|
118
|
+
ctx = ReplayContext.current
|
|
119
|
+
return nil unless ctx
|
|
120
|
+
|
|
121
|
+
lease = ctx[:db_branch_lease]
|
|
122
|
+
return nil unless lease
|
|
123
|
+
|
|
124
|
+
# Surface the Bitfab trace ID (what the customer sees in the dashboard),
|
|
125
|
+
# not the external one. Falling back to the external ID keeps replays from
|
|
126
|
+
# external sources working until that path is fully wired.
|
|
127
|
+
trace_id = ctx[:source_bitfab_trace_id] || ctx[:input_source_trace_id]
|
|
128
|
+
return nil unless trace_id
|
|
129
|
+
|
|
130
|
+
ReplayBranch.new(lease, trace_id, ctx)
|
|
131
|
+
end
|
|
132
|
+
|
|
105
133
|
# Get a handle to the current active trace.
|
|
106
134
|
#
|
|
107
135
|
# Call this from inside a traced method to get a trace handle that allows
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bitfab
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.33.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Harvest Team
|
|
@@ -123,7 +123,7 @@ files:
|
|
|
123
123
|
- lib/bitfab/http_client.rb
|
|
124
124
|
- lib/bitfab/mock_override.rb
|
|
125
125
|
- lib/bitfab/replay.rb
|
|
126
|
-
- lib/bitfab/
|
|
126
|
+
- lib/bitfab/replay_branch.rb
|
|
127
127
|
- lib/bitfab/serialize.rb
|
|
128
128
|
- lib/bitfab/span_context.rb
|
|
129
129
|
- lib/bitfab/traceable.rb
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Bitfab
|
|
4
|
-
# Per-trace environment exposed to customer code during replay.
|
|
5
|
-
#
|
|
6
|
-
# The customer instantiates one +ReplayEnvironment+ and passes it to
|
|
7
|
-
# +client.replay(environment: ...)+. Inside the replayed method they read
|
|
8
|
-
# +env.database_url+ (and friends) to pick up the per-trace branch URL the
|
|
9
|
-
# Bitfab service resolved from the source trace's snapshot reference.
|
|
10
|
-
#
|
|
11
|
-
# Outside replay, reading +env.database_url+ raises. Customer code uses the
|
|
12
|
-
# env only on the replay path; live request code keeps reading
|
|
13
|
-
# +ENV["DATABASE_URL"]+ the normal way.
|
|
14
|
-
#
|
|
15
|
-
# Concurrency-safe: the readers resolve through the thread-local replay
|
|
16
|
-
# context, so each in-flight replay item sees its own per-trace values even
|
|
17
|
-
# when the SDK runs items across worker threads.
|
|
18
|
-
#
|
|
19
|
-
# The constructor takes how each branch should be sized and warmed. That
|
|
20
|
-
# lives here rather than on the replay arguments because the settings only
|
|
21
|
-
# mean anything for a replay that has an environment.
|
|
22
|
-
#
|
|
23
|
-
# Internally the resolved per-item state is a DB branch lease (the SDK <->
|
|
24
|
-
# server protocol term). We expose its useful fields directly here so
|
|
25
|
-
# customer code never sees the word.
|
|
26
|
-
class ReplayEnvironment
|
|
27
|
-
# The branch settings this environment was constructed with, as a
|
|
28
|
-
# symbol-keyed hash, readable anywhere. Every other reader reports the
|
|
29
|
-
# lease the server resolved and is therefore replay-only.
|
|
30
|
-
attr_reader :db_branch
|
|
31
|
-
|
|
32
|
-
# @param min_cu [Numeric, nil] autoscaling floor for the branch's compute,
|
|
33
|
-
# in Neon Compute Units (0.25 to 56). Omit to keep the mirror project's
|
|
34
|
-
# own default. Raise it when the mirror is provisioned smaller than the
|
|
35
|
-
# database it stands in for, so replay latency reflects your code rather
|
|
36
|
-
# than a cold, undersized branch.
|
|
37
|
-
# @param max_cu [Numeric, nil] autoscaling ceiling, in Neon Compute Units.
|
|
38
|
-
# Equal to +min_cu+ pins the size, which keeps items comparable:
|
|
39
|
-
# otherwise a later item can run against an endpoint that already scaled
|
|
40
|
-
# up and post a better number for the same code.
|
|
41
|
-
# @param warmup_sql [String, nil] SQL that warms the branch's cache. The
|
|
42
|
-
# server appends it to the branch's readiness check, so it runs BEFORE
|
|
43
|
-
# your method sees the lease and its time is not charged to the replayed
|
|
44
|
-
# call. Invalid SQL fails the lease rather than silently leaving the
|
|
45
|
-
# branch cold.
|
|
46
|
-
def initialize(min_cu: nil, max_cu: nil, warmup_sql: nil)
|
|
47
|
-
@db_branch = {}
|
|
48
|
-
@db_branch[:min_cu] = min_cu unless min_cu.nil?
|
|
49
|
-
@db_branch[:max_cu] = max_cu unless max_cu.nil?
|
|
50
|
-
@db_branch[:warmup_sql] = warmup_sql unless warmup_sql.nil?
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# The per-trace branch URL for the item currently being replayed.
|
|
54
|
-
# Raises if read outside a replay item.
|
|
55
|
-
def database_url
|
|
56
|
-
snap = require_snapshot
|
|
57
|
-
mark_accessed
|
|
58
|
-
snap.fetch(:database_url)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# When the per-trace branch URL stops being valid. ISO-8601.
|
|
62
|
-
def expires_at
|
|
63
|
-
require_snapshot.fetch(:expires_at)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Deep link to the branch in the provider console, if available.
|
|
67
|
-
def provider_console_url
|
|
68
|
-
require_snapshot[:provider_console_url]
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# True if the branch is read-only. Customer code can use this to skip write
|
|
72
|
-
# operations during replay when the provider returned a read-only lease.
|
|
73
|
-
def read_only
|
|
74
|
-
require_snapshot[:read_only]
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
# The branch's region, e.g. "aws-us-east-1". A compute runs in its
|
|
78
|
-
# project's region, so a replay runner elsewhere pays that round trip on
|
|
79
|
-
# every query.
|
|
80
|
-
def region
|
|
81
|
-
require_snapshot[:region]
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# The historical trace ID that produced the input for this replay item.
|
|
85
|
-
def trace_id
|
|
86
|
-
require_snapshot.fetch(:trace_id)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
# True when read inside a replay item that has a resolved branch.
|
|
90
|
-
def active?
|
|
91
|
-
!read_snapshot.nil?
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
# Non-raising variant for callers that handle the inactive case. Returns a
|
|
95
|
-
# symbol-keyed hash or nil.
|
|
96
|
-
def snapshot
|
|
97
|
-
snap = read_snapshot
|
|
98
|
-
mark_accessed if snap
|
|
99
|
-
snap
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
private
|
|
103
|
-
|
|
104
|
-
# Record on the replay context that customer code obtained the branch
|
|
105
|
-
# URL. Only +database_url+ and +snapshot+ count: +active?+, +read_only+
|
|
106
|
-
# and friends inspect the lease without exposing the connection string,
|
|
107
|
-
# so they don't prove the replayed code could have connected to the
|
|
108
|
-
# branch.
|
|
109
|
-
def mark_accessed
|
|
110
|
-
ctx = ReplayContext.current
|
|
111
|
-
return unless ctx && ctx[:db_branch_lease]
|
|
112
|
-
|
|
113
|
-
ctx[:db_snapshot_accessed] = true
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
def read_snapshot
|
|
117
|
-
ctx = ReplayContext.current
|
|
118
|
-
return nil unless ctx
|
|
119
|
-
|
|
120
|
-
lease = ctx[:db_branch_lease]
|
|
121
|
-
return nil unless lease
|
|
122
|
-
|
|
123
|
-
# Surface the Bitfab trace ID (what the customer sees in the dashboard),
|
|
124
|
-
# falling back to the external trace ID only if the Bitfab ID is somehow
|
|
125
|
-
# absent: keeps replays from external sources working until the
|
|
126
|
-
# source-system path is fully wired.
|
|
127
|
-
trace_id = ctx[:source_bitfab_trace_id] || ctx[:input_source_trace_id]
|
|
128
|
-
return nil unless trace_id
|
|
129
|
-
|
|
130
|
-
{
|
|
131
|
-
database_url: lease["databaseUrl"],
|
|
132
|
-
expires_at: lease["expiresAt"],
|
|
133
|
-
provider_console_url: lease["providerConsoleUrl"],
|
|
134
|
-
read_only: lease["readOnly"],
|
|
135
|
-
region: lease["region"],
|
|
136
|
-
trace_id:
|
|
137
|
-
}
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def require_snapshot
|
|
141
|
-
snap = read_snapshot
|
|
142
|
-
return snap if snap
|
|
143
|
-
|
|
144
|
-
raise "ReplayEnvironment accessed outside of a replay item. Pass it to " \
|
|
145
|
-
"client.replay(environment: ...) and only read it inside the replayed method."
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
end
|