Module: Isolator

Extended by:
Callbacks, Isolate
Defined in:
lib/isolator.rb,
lib/isolator/errors.rb,
lib/isolator/ignorer.rb,
lib/isolator/isolate.rb,
lib/isolator/railtie.rb,
lib/isolator/version.rb,
lib/isolator/notifier.rb,
lib/isolator/callbacks.rb,
lib/isolator/adapters/base.rb,
lib/isolator/configuration.rb,
lib/isolator/simple_hashie.rb,
lib/isolator/adapter_builder.rb,
lib/isolator/ext/thread_fetch.rb,
lib/isolator/plugins/database_subtransactions.rb,
lib/isolator/orm_adapters/active_support_subscriber.rb,
lib/isolator/plugins/concurrent_database_transactions.rb,
lib/isolator/orm_adapters/active_support_transaction_subscriber.rb

Overview

:nodoc: all

Defined Under Namespace

Modules: ActiveSupportSubscriber, ActiveSupportTransactionSubscriber, AdapterBuilder, Adapters, Callbacks, Isolate, ThreadFetch Classes: BackgroundJobError, ConcurrentTransactionError, Configuration, HTTPError, Ignorer, MailerError, MaxSubtransactionsExceededError, Notifier, Railtie, SimpleHashie, ThreadStateProxy, UnsafeOperationError, WebsocketError

Constant Summary collapse

VERSION =
"1.2.0"

Class Attribute Summary collapse

Class Method Summary collapse

Methods included from Callbacks

after_isolate, after_isolate_callbacks, before_isolate, before_isolate_callbacks, finish!, notify!, on_transaction_begin, on_transaction_end, start!, transaction_begin_callbacks, transaction_end_callbacks

Methods included from Isolate

isolate, remove_adapter

Class Attribute Details

.backtrace_cleanerObject

Returns the value of attribute backtrace_cleaner.



212
213
214
# File 'lib/isolator.rb', line 212

def backtrace_cleaner
  @backtrace_cleaner
end

.backtrace_lengthObject

Returns the value of attribute backtrace_length.



212
213
214
# File 'lib/isolator.rb', line 212

def backtrace_length
  @backtrace_length
end

.debug_enabledObject

Returns the value of attribute debug_enabled.



212
213
214
# File 'lib/isolator.rb', line 212

def debug_enabled
  @debug_enabled
end

.default_connection_idObject

Returns the value of attribute default_connection_id.



37
38
39
# File 'lib/isolator.rb', line 37

def default_connection_id
  @default_connection_id
end

.default_thresholdObject

Returns the value of attribute default_threshold.



37
38
39
# File 'lib/isolator.rb', line 37

def default_threshold
  @default_threshold
end

Class Method Details

.adaptersObject



196
197
198
# File 'lib/isolator.rb', line 196

def adapters
  @adapters ||= Isolator::SimpleHashie.new
end

.all_transactionsObject



97
98
99
# File 'lib/isolator.rb', line 97

def all_transactions
  state[:transactions] || {}
end

.clear_transactions!Object



177
178
179
# File 'lib/isolator.rb', line 177

def clear_transactions!
  state[:transactions]&.clear
end

.configObject



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

def config
  @config ||= Configuration.new
end

.configure {|config| ... } ⇒ Object

Yields:



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

def configure
  yield config
end

.connection_threshold(connection_id) ⇒ Object



101
102
103
# File 'lib/isolator.rb', line 101

def connection_threshold(connection_id)
  state[:thresholds]&.[](connection_id) || default_threshold
end

.current_transactions(connection_id = default_connection_id.call) ⇒ Object



93
94
95
# File 'lib/isolator.rb', line 93

def current_transactions(connection_id = default_connection_id.call)
  state[:transactions]&.[](connection_id) || 0
end

.decr_thresholds!Object



121
122
123
124
125
126
127
128
# File 'lib/isolator.rb', line 121

def decr_thresholds!
  self.default_threshold -= 1
  debug!("Thresholds were incremented")

  return unless state[:thresholds]

  state[:thresholds].transform_values!(&:pred)
end

.decr_transactions!(connection_id = default_connection_id.call) ⇒ Object



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/isolator.rb', line 152

def decr_transactions!(connection_id = default_connection_id.call)
  current = state[:transactions]&.[](connection_id) || 0

  if current <= 0
    warn "Trying to finalize an untracked transaction"
    return
  end

  state[:transactions][connection_id] -= 1

  current_depth = current_transactions(connection_id)
  threshold = connection_threshold(connection_id)

  if current_depth >= threshold - 1
    event = {connection_id: connection_id, depth: current_depth - threshold + 1}.freeze
    notify!(:end, event)
  end

  finish! if current_depth == (threshold - 1)

  state[:transactions].delete(connection_id) if state[:transactions][connection_id].zero?

  debug!("Transaction closed for connection #{connection_id} (total: #{state[:transactions][connection_id]}, threshold: #{state[:thresholds]&.[](connection_id) || default_threshold})")
end

.disableObject

Accepts block and disable Isolator within



60
61
62
63
64
65
66
67
68
69
70
# File 'lib/isolator.rb', line 60

def disable
  return yield if disabled?
  res = nil
  begin
    disable!
    res = yield
  ensure
    enable!
  end
  res
end

.disable!Object



55
56
57
# File 'lib/isolator.rb', line 55

def disable!
  state[:disabled] = true
end

.disabled?Boolean

Returns:

  • (Boolean)


192
193
194
# File 'lib/isolator.rb', line 192

def disabled?
  state[:disabled] == true
end

.enableObject

Accepts block and enable Isolator within



73
74
75
76
77
78
79
80
81
82
83
# File 'lib/isolator.rb', line 73

def enable
  return yield if enabled?
  res = nil
  begin
    enable!
    res = yield
  ensure
    disable!
  end
  res
end

.enable!Object



51
52
53
# File 'lib/isolator.rb', line 51

def enable!
  state[:disabled] = false
end

.enabled?Boolean

Returns:

  • (Boolean)


188
189
190
# File 'lib/isolator.rb', line 188

def enabled?
  !disabled?
end

.has_adapter?(id) ⇒ Boolean

Returns:

  • (Boolean)


200
201
202
# File 'lib/isolator.rb', line 200

def has_adapter?(id)
  adapters.key?(id.to_s)
end

.incr_thresholds!Object



112
113
114
115
116
117
118
119
# File 'lib/isolator.rb', line 112

def incr_thresholds!
  self.default_threshold += 1
  debug!("Thresholds were incremented")

  return unless state[:thresholds]

  state[:thresholds].transform_values!(&:succ)
end

.incr_transactions!(connection_id = default_connection_id.call) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/isolator.rb', line 130

def incr_transactions!(connection_id = default_connection_id.call)
  state[:transactions] ||= Hash.new { |h, k| h[k] = 0 }
  state[:transactions][connection_id] += 1

  # Workaround to track threshold changes made before opening a connection
  pending_threshold = state[:thresholds]&.delete(0)
  if pending_threshold
    state[:thresholds][connection_id] = pending_threshold
  end

  debug!("Transaction opened for connection #{connection_id} (total: #{state[:transactions][connection_id]}, threshold: #{state[:thresholds]&.fetch(connection_id, default_threshold)})")

  current_depth = current_transactions(connection_id)
  threshold = connection_threshold(connection_id)

  start! if current_depth == threshold
  if current_depth >= threshold
    event = {connection_id: connection_id, depth: current_depth - threshold + 1}.freeze
    notify!(:begin, event)
  end
end

.load_ignore_config(path) ⇒ Object



204
205
206
207
# File 'lib/isolator.rb', line 204

def load_ignore_config(path)
  warn "[DEPRECATION] `load_ignore_config` is deprecated. Please use `Isolator::Ignorer.prepare` instead."
  Isolator::Ignorer.prepare(path: path)
end

.notify(exception:, backtrace:) ⇒ Object



47
48
49
# File 'lib/isolator.rb', line 47

def notify(exception:, backtrace:)
  Notifier.new(exception, backtrace).call
end

.set_connection_threshold(val, connection_id = default_connection_id.call) ⇒ Object



105
106
107
108
109
110
# File 'lib/isolator.rb', line 105

def set_connection_threshold(val, connection_id = default_connection_id.call)
  state[:thresholds] ||= Hash.new { |h, k| h[k] = Isolator.default_threshold }
  state[:thresholds][connection_id] = val

  debug!("Threshold value was changed for connection #{connection_id}: #{val}")
end

.transactions_threshold(connection_id = default_connection_id.call) ⇒ Object



89
90
91
# File 'lib/isolator.rb', line 89

def transactions_threshold(connection_id = default_connection_id.call)
  connection_threshold(connection_id)
end

.transactions_threshold=(val) ⇒ Object



85
86
87
# File 'lib/isolator.rb', line 85

def transactions_threshold=(val)
  set_connection_threshold(val)
end

.within_transaction?Boolean

Returns:

  • (Boolean)


181
182
183
184
185
186
# File 'lib/isolator.rb', line 181

def within_transaction?
  state[:transactions]&.each do |connection_id, transaction_count|
    return true if transaction_count >= connection_threshold(connection_id)
  end
  false
end