Class: Bitfab::ReplayBranch

Inherits:
Object
  • Object
show all
Defined in:
lib/bitfab/replay_branch.rb

Overview

The database branch a single replay item runs against.

Bitfab.current_replay_branch hands you one inside a replayed method when the source trace carried a DB snapshot reference and the Bitfab service resolved a branch from it. Outside a replay item, or when no branch was resolved, that reader returns nil and your code keeps reading ENV["DATABASE_URL"] the normal way.

Immutable and scoped to one item: the reader builds it from the thread-local replay context, so parallel replay items each see their own branch and no lease state lives on a long-lived object.

Internally the resolved per-item state is a DB branch lease (the SDK/server protocol term). Its useful fields are exposed directly here so customer code never sees the word.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(lease, trace_id, context) ⇒ ReplayBranch

Built by Bitfab.current_replay_branch; never constructed by callers.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/bitfab/replay_branch.rb', line 49

def initialize(lease, trace_id, context)
  # Copy the lease wholesale minus the connection string, so a field the
  # server starts sending reaches customer code without an SDK release.
  # Everything set here must stay plain data: #database_url is the only
  # member allowed to mark the branch as accessed.
  # The internals are underscore-prefixed because the loop below writes an
  # ivar per lease key: snake_case never emits a leading underscore, so no
  # field the server invents can collide with them. A plain @fields would
  # be clobbered mid-loop by a key named "fields", corrupting #as_json.
  fields = {}
  lease.each do |key, value|
    next if key == "databaseUrl"

    name = snake_case(key)
    fields[name] = value
    instance_variable_set(:"@#{name}", value)
    define_singleton_method(name) { value } unless respond_to?(name)
  end
  @trace_id = trace_id
  fields["trace_id"] = trace_id
  @_fields = fields.freeze
  @_url = lease["databaseUrl"]
  @_context = context
  freeze
end

Instance Attribute Details

#env_keyObject (readonly)

Env var name the customer's app reads, e.g. "DATABASE_URL".



24
25
26
# File 'lib/bitfab/replay_branch.rb', line 24

def env_key
  @env_key
end

#expires_atObject (readonly)

When this branch's URL stops being valid. ISO-8601.



27
28
29
# File 'lib/bitfab/replay_branch.rb', line 27

def expires_at
  @expires_at
end

#neon_branch_idObject (readonly)

The provider's own id for this branch, e.g. for correlating with its console.



21
22
23
# File 'lib/bitfab/replay_branch.rb', line 21

def neon_branch_id
  @neon_branch_id
end

#provider_console_urlObject (readonly)

Deep link to the branch in the provider console, if available.



35
36
37
# File 'lib/bitfab/replay_branch.rb', line 35

def provider_console_url
  @provider_console_url
end

#read_onlyObject (readonly)

True if the branch is read-only. Use it to skip write operations during replay when the provider returned a read-only lease.



39
40
41
# File 'lib/bitfab/replay_branch.rb', line 39

def read_only
  @read_only
end

#regionObject (readonly)

The branch's region, e.g. "aws-us-east-1". A compute runs in its project's region, so a replay runner elsewhere pays that round trip on every query.



43
44
45
# File 'lib/bitfab/replay_branch.rb', line 43

def region
  @region
end

#snapshot_timestampObject (readonly)

The instant this branch is pinned to: the source trace's wall clock, read just before the traced method ran. Compare it against the trace you meant to replay to confirm the branch is the right point in history.



32
33
34
# File 'lib/bitfab/replay_branch.rb', line 32

def snapshot_timestamp
  @snapshot_timestamp
end

#trace_idObject (readonly)

The historical trace ID that produced the input for this replay item.



46
47
48
# File 'lib/bitfab/replay_branch.rb', line 46

def trace_id
  @trace_id
end

Instance Method Details

#as_json(_options = nil) ⇒ Object

ActiveSupport gives every object a to_json that serializes its instance variables, so under Rails a branch reaching a log line or an API response would carry the connection string and the whole replay context. Serialize the exposed fields only, the same set inspect shows.



99
100
101
# File 'lib/bitfab/replay_branch.rb', line 99

def as_json(_options = nil)
  @_fields.dup
end

#database_urlObject

Connection string for this item's branch. Point your database client at it instead of the live database for the duration of the replayed call.

Reading it records on the trace that the replayed code obtained the branch URL, which is what separates "a branch was provisioned" from "the branch was actually used". The other readers inspect the lease without exposing the connection string, so they deliberately do not record anything.



82
83
84
85
# File 'lib/bitfab/replay_branch.rb', line 82

def database_url
  @_context[:db_snapshot_accessed] = true
  @_url
end

#inspectObject

Redact the connection string so a logged or inspected branch cannot leak it.



88
89
90
91
92
93
# File 'lib/bitfab/replay_branch.rb', line 88

def inspect
  "#<Bitfab::ReplayBranch trace_id=#{@trace_id.inspect} " \
    "expires_at=#{@expires_at.inspect} region=#{@region.inspect} " \
    "read_only=#{@read_only.inspect} " \
    "snapshot_timestamp=#{@snapshot_timestamp.inspect}>"
end

#to_json(*args) ⇒ Object



103
104
105
# File 'lib/bitfab/replay_branch.rb', line 103

def to_json(*args)
  as_json.to_json(*args)
end