bitfab 0.36.0 → 0.36.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1b5fdfbac8e62c59768ba554cd7c68b6fb7de3f1a37befd01a66cc6097821499
4
- data.tar.gz: 31bb825d1fcde3c946c5467bb6e1ccbd56816815b38ba4f3eb5395bdb5061907
3
+ metadata.gz: 44af81a548095fe4bf8a347b6e58bb1c3bdcf2726becd78f029ec5c7c9507ca9
4
+ data.tar.gz: 4a00a95452cf45e5992e7eee4754850923eed8767713368a063fa1fed84d4b7c
5
5
  SHA512:
6
- metadata.gz: efd78672cf98417507533b22bc0d4783ad7c29a18c4e9f419d95e328831275c26b4d15888642f07e19cd7a85b62babe55e223576d6a4885228fea4243ab84607
7
- data.tar.gz: 83dc9683122eefbe92fc747d783fb81351acc6ccef94e1c31f5ae2b391adac9d775832d9f56818cde2541817a4b8cde475cda44021b93a5f23bcbac9a010904a
6
+ metadata.gz: 9043c05ffb79c8830299127c39de0dc77b77dc3fa1fd2a573813696bcd9a7ba3fa95d7a06a7371ff596ed04781f439311f773bdff24afe055b252b36e0d36bb5
7
+ data.tar.gz: d73327657254114a22c091bb58503c17f1f8d0fcc19f0179c283b452df4c7240d446ff8d36962b16fcc47ea13e0315c80f554280b79f47b684f24ba50b11ce23
data/lib/bitfab/client.rb CHANGED
@@ -407,6 +407,7 @@ module Bitfab
407
407
  {
408
408
  neon_branch_id: lease["neonBranchId"],
409
409
  snapshot_timestamp: lease["snapshotTimestamp"],
410
+ region: lease["region"],
410
411
  original_trace_id: replay_ctx[:source_bitfab_trace_id],
411
412
  # Deprecated wire alias, kept so this SDK still reports usage
412
413
  # against servers that predate the rename.
@@ -593,6 +594,9 @@ module Bitfab
593
594
  if db_snapshot_usage[:snapshot_timestamp]
594
595
  usage["snapshot_timestamp"] = db_snapshot_usage[:snapshot_timestamp]
595
596
  end
597
+ if db_snapshot_usage[:region]
598
+ usage["region"] = db_snapshot_usage[:region]
599
+ end
596
600
  if db_snapshot_usage[:original_trace_id]
597
601
  usage["original_trace_id"] = db_snapshot_usage[:original_trace_id]
598
602
  # Deprecated wire alias, kept so this SDK still reports usage
@@ -181,9 +181,11 @@ module Bitfab
181
181
  request("/api/sdk/replay/start", payload, timeout:)
182
182
  end
183
183
 
184
- # Fetch an external span by ID. Blocking GET request.
185
- def get_external_span(span_id)
186
- get("/api/sdk/externalSpans/#{span_id}", timeout: 30)
184
+ # Fetch an external span by ID. Blocking GET request. The replay view keeps
185
+ # only the input/output serialization fields used by replay.
186
+ def get_external_span(span_id, replay_view: false)
187
+ query = replay_view ? "?view=replay" : ""
188
+ get("/api/sdk/externalSpans/#{span_id}#{query}", timeout: 30)
187
189
  end
188
190
 
189
191
  def get_trace_span(trace_id, id: nil, name: nil, occurrence: "last")
@@ -210,9 +212,12 @@ module Bitfab
210
212
  # payloads so only the spans actually mocked are fetched later (by
211
213
  # externalSpanId), avoiding dragging down every span's output when only a
212
214
  # few are mocked.
213
- def get_span_tree(external_span_id, include_outputs: true)
215
+ def get_span_tree(external_span_id, include_outputs: true, include_root_output: true)
214
216
  endpoint = "/api/sdk/replay/spanTree/#{external_span_id}"
215
- endpoint += "?includeOutputs=false" unless include_outputs
217
+ query = []
218
+ query << "includeOutputs=false" unless include_outputs
219
+ query << "includeRootOutput=false" unless include_root_output
220
+ endpoint += "?#{query.join("&")}" unless query.empty?
216
221
  get(endpoint, timeout: 30)
217
222
  end
218
223
 
data/lib/bitfab/replay.rb CHANGED
@@ -62,10 +62,12 @@ module Bitfab
62
62
  # (via +database_url+). Reported on the trace
63
63
  # completion inside the +db_snapshot_usage+ record (its +accessed+
64
64
  # field) so the server can distinguish "branch was provisioned and
65
- # exposed" from "branch URL was actually consumed". Any future
66
- # consumption path that hands the URL to customer code by other means
67
- # (e.g. a process-isolated runner writing an env overlay) must also
68
- # set this.
65
+ # exposed" from "branch URL was actually consumed". Only an explicit
66
+ # +database_url+ read may set it. A path that hands the URL over by other
67
+ # means (e.g. a process-isolated runner writing an env overlay) must
68
+ # leave it alone: setting it there would make every such replay report
69
+ # accessed for free, and the flag would stop separating "branch was
70
+ # used" from "branch was offered".
69
71
  ctx[:db_branch_lease] = db_branch_lease if db_branch_lease
70
72
  ctx[:source_bitfab_trace_id] = source_bitfab_trace_id if source_bitfab_trace_id
71
73
  Thread.current[REPLAY_CONTEXT_KEY] = ctx
@@ -674,7 +676,7 @@ module Bitfab
674
676
  "produce a result that looks valid but did not use the historical data you asked for."
675
677
  end
676
678
 
677
- span = http_client.get_external_span(original_span_id)
679
+ span = http_client.get_external_span(original_span_id, replay_view: true)
678
680
  item_data = extract_span_data(span)
679
681
 
680
682
  # Fetch the span tree when the base strategy needs recorded outputs
@@ -692,7 +694,11 @@ module Bitfab
692
694
  mock_tree = nil
693
695
  if mock_strategy == "all" || mock_strategy == "marked" || overrides_present
694
696
  begin
695
- tree = http_client.get_span_tree(original_span_id, include_outputs:)
697
+ tree = http_client.get_span_tree(
698
+ original_span_id,
699
+ include_outputs:,
700
+ include_root_output: false
701
+ )
696
702
  mock_tree = build_mock_tree(tree["root"] || {})
697
703
  rescue Exception => e # rubocop:disable Lint/RescueException
698
704
  raise if e.is_a?(SystemExit) || e.is_a?(SignalException)
@@ -841,7 +847,7 @@ module Bitfab
841
847
  lambda do |external_span_id|
842
848
  return cache[external_span_id] if cache.key?(external_span_id)
843
849
 
844
- span = http_client.get_external_span(external_span_id)
850
+ span = http_client.get_external_span(external_span_id, replay_view: true)
845
851
  span_data = (span["rawData"] || {})["span_data"] || {}
846
852
  # Prefer the Ruby Marshal payload (output_serialized) written by this
847
853
  # SDK; fall back to another SDK's output_meta, then the raw JSON output.
@@ -17,9 +17,20 @@ module Bitfab
17
17
  # protocol term). Its useful fields are exposed directly here so customer code
18
18
  # never sees the word.
19
19
  class ReplayBranch
20
+ # The provider's own id for this branch, e.g. for correlating with its console.
21
+ attr_reader :neon_branch_id
22
+
23
+ # Env var name the customer's app reads, e.g. "DATABASE_URL".
24
+ attr_reader :env_key
25
+
20
26
  # When this branch's URL stops being valid. ISO-8601.
21
27
  attr_reader :expires_at
22
28
 
29
+ # The instant this branch is pinned to: the source trace's wall clock, read
30
+ # just before the traced method ran. Compare it against the trace you meant
31
+ # to replay to confirm the branch is the right point in history.
32
+ attr_reader :snapshot_timestamp
33
+
23
34
  # Deep link to the branch in the provider console, if available.
24
35
  attr_reader :provider_console_url
25
36
 
@@ -36,13 +47,28 @@ module Bitfab
36
47
 
37
48
  # Built by Bitfab.current_replay_branch; never constructed by callers.
38
49
  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"]
50
+ # Copy the lease wholesale minus the connection string, so a field the
51
+ # server starts sending reaches customer code without an SDK release.
52
+ # Everything set here must stay plain data: #database_url is the only
53
+ # member allowed to mark the branch as accessed.
54
+ # The internals are underscore-prefixed because the loop below writes an
55
+ # ivar per lease key: snake_case never emits a leading underscore, so no
56
+ # field the server invents can collide with them. A plain @fields would
57
+ # be clobbered mid-loop by a key named "fields", corrupting #as_json.
58
+ fields = {}
59
+ lease.each do |key, value|
60
+ next if key == "databaseUrl"
61
+
62
+ name = snake_case(key)
63
+ fields[name] = value
64
+ instance_variable_set(:"@#{name}", value)
65
+ define_singleton_method(name) { value } unless respond_to?(name)
66
+ end
43
67
  @trace_id = trace_id
44
- @url = lease["databaseUrl"]
45
- @context = context
68
+ fields["trace_id"] = trace_id
69
+ @_fields = fields.freeze
70
+ @_url = lease["databaseUrl"]
71
+ @_context = context
46
72
  freeze
47
73
  end
48
74
 
@@ -54,15 +80,39 @@ module Bitfab
54
80
  # was actually used". The other readers inspect the lease without exposing
55
81
  # the connection string, so they deliberately do not record anything.
56
82
  def database_url
57
- @context[:db_snapshot_accessed] = true
58
- @url
83
+ @_context[:db_snapshot_accessed] = true
84
+ @_url
59
85
  end
60
86
 
61
87
  # Redact the connection string so a logged or inspected branch cannot leak it.
62
88
  def inspect
63
89
  "#<Bitfab::ReplayBranch trace_id=#{@trace_id.inspect} " \
64
90
  "expires_at=#{@expires_at.inspect} region=#{@region.inspect} " \
65
- "read_only=#{@read_only.inspect}>"
91
+ "read_only=#{@read_only.inspect} " \
92
+ "snapshot_timestamp=#{@snapshot_timestamp.inspect}>"
93
+ end
94
+
95
+ # ActiveSupport gives every object a +to_json+ that serializes its instance
96
+ # variables, so under Rails a branch reaching a log line or an API response
97
+ # would carry the connection string and the whole replay context. Serialize
98
+ # the exposed fields only, the same set +inspect+ shows.
99
+ def as_json(_options = nil)
100
+ @_fields.dup
101
+ end
102
+
103
+ def to_json(*args)
104
+ as_json.to_json(*args)
105
+ end
106
+
107
+ private
108
+
109
+ # Two passes so a run of capitals stays one word: "dbURL" is "db_url", not
110
+ # "db_u_r_l". Must match the Python SDK's conversion exactly, or the same
111
+ # server field would arrive under different names in the two SDKs.
112
+ def snake_case(key)
113
+ key.gsub(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
114
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
115
+ .downcase
66
116
  end
67
117
  end
68
118
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bitfab
4
- VERSION = "0.36.0"
4
+ VERSION = "0.36.2"
5
5
  end
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.36.0
4
+ version: 0.36.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harvest Team