isolator 0.9.0 → 0.11.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/CHANGELOG.md +18 -0
- data/lib/isolator/adapter_builder.rb +2 -1
- data/lib/isolator/adapters/http/webmock.rb +2 -1
- data/lib/isolator/database_cleaner_support.rb +10 -3
- data/lib/isolator/notifier.rb +14 -6
- data/lib/isolator/orm_adapters/active_record.rb +7 -1
- data/lib/isolator/orm_adapters/active_support_subscriber.rb +39 -5
- data/lib/isolator/orm_adapters/active_support_transaction_subscriber.rb +74 -0
- data/lib/isolator/version.rb +1 -1
- data/lib/isolator.rb +14 -6
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb3ecc2b4b93f35241a5f03804bc89fab1ab2a6d9d4fe33753ad827bc8a06ea5
|
4
|
+
data.tar.gz: 8385b3b3654b25d1ec2ec178305cac41049523339da8d56c6ba7defb31996640
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a18d105a45e116af3120e4f9eefd69f1c533b14f1ce26b8bdd9c284fd99f87ddd8e3650ae3e7a73cc54592329726f549d16b374763f5bae6c71163e36499b55a
|
7
|
+
data.tar.gz: cb72be0a0d3aa004dab07392b875e0d70472806517d7d89acdafd133f332a50f59232aed8c919c12ce15cd12399b29f7ef8e33f986e445046b9171765db4ae74
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,22 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 0.11.0 (2023-09-27)
|
6
|
+
|
7
|
+
- Use Rails new `transaction.active_record` event if available to better handle edge cases. ([@palkan][])
|
8
|
+
|
9
|
+
- Fix logging non-UTF8 strings. ([@palkan][])
|
10
|
+
|
11
|
+
Fixes [#66](https://github.com/palkan/isolator/issues/66)
|
12
|
+
|
13
|
+
## 0.10.0 (2023-08-15)
|
14
|
+
|
15
|
+
- Support multiple databases with DatabaseCleaner. ([@palkan][])
|
16
|
+
|
17
|
+
- Fix query having invalid characters. ([@tagirahmad][])
|
18
|
+
|
19
|
+
Fixes [#43](https://github.com/palkan/isolator/issues/43).
|
20
|
+
|
5
21
|
## 0.9.0 (2023-05-18)
|
6
22
|
|
7
23
|
- Support keyword arguments to isolated method in Ruby 3.0. ([@Mange][])
|
@@ -109,3 +125,5 @@ This, for example, makes Isolator compatible with Rails multi-database apps.
|
|
109
125
|
[@mquan]: https://github.com/mquan
|
110
126
|
[@bobbymcwho]: https://github.com/bobbymcwho
|
111
127
|
[@Mange]: https://github.com/Mange
|
128
|
+
[@tomgi]: https://github.com/tomgi
|
129
|
+
[@tagirahmad]: https://github.com/tagirahmad
|
@@ -33,7 +33,8 @@ module Isolator
|
|
33
33
|
|
34
34
|
Module.new do
|
35
35
|
define_method method_name do |*args, **kwargs, &block|
|
36
|
-
|
36
|
+
# check if we are even notifying before calling `caller`, which is well known to be slow
|
37
|
+
adapter.notify(caller, self, *args, **kwargs) if adapter.notify?(*args, **kwargs)
|
37
38
|
super(*args, **kwargs, &block)
|
38
39
|
end
|
39
40
|
end
|
@@ -1,16 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "database_cleaner/active_record/transaction"
|
4
|
-
|
5
4
|
::DatabaseCleaner::ActiveRecord::Transaction.prepend(
|
6
5
|
Module.new do
|
7
6
|
def start
|
8
7
|
super
|
9
|
-
|
8
|
+
connection_id = connection_class.connection.object_id
|
9
|
+
Isolator.set_connection_threshold(
|
10
|
+
Isolator.transactions_threshold(connection_id) + 1,
|
11
|
+
connection_id
|
12
|
+
)
|
10
13
|
end
|
11
14
|
|
12
15
|
def clean
|
13
|
-
|
16
|
+
connection_id = connection_class.connection.object_id
|
17
|
+
Isolator.set_connection_threshold(
|
18
|
+
Isolator.transactions_threshold(connection_id) - 1,
|
19
|
+
connection_id
|
20
|
+
)
|
14
21
|
super
|
15
22
|
end
|
16
23
|
end
|
data/lib/isolator/notifier.rb
CHANGED
@@ -29,14 +29,22 @@ module Isolator
|
|
29
29
|
def log_exception
|
30
30
|
return unless Isolator.config.logger
|
31
31
|
|
32
|
-
|
33
|
-
"#{exception.message}"
|
32
|
+
separator = " ↳ "
|
34
33
|
|
35
|
-
|
36
|
-
msg
|
37
|
-
|
34
|
+
begin
|
35
|
+
msg = "[ISOLATOR EXCEPTION]\n" \
|
36
|
+
"#{exception.message}"
|
38
37
|
|
39
|
-
|
38
|
+
filtered_backtrace.each do |offense_line|
|
39
|
+
msg += "\n #{separator}#{offense_line}"
|
40
|
+
end
|
41
|
+
|
42
|
+
Isolator.config.logger.warn(msg)
|
43
|
+
rescue Encoding::CompatibilityError
|
44
|
+
should_retry = separator != " - "
|
45
|
+
separator = " - "
|
46
|
+
retry if should_retry
|
47
|
+
end
|
40
48
|
end
|
41
49
|
|
42
50
|
def send_notifications
|
@@ -2,4 +2,10 @@
|
|
2
2
|
|
3
3
|
require_relative "./active_support_subscriber"
|
4
4
|
|
5
|
-
|
5
|
+
# We rely on this feature introduced in 7.1.0.beta1: https://github.com/rails/rails/pull/49192
|
6
|
+
if ActiveRecord::VERSION::MAJOR >= 7 && ActiveRecord::VERSION::MINOR >= 1
|
7
|
+
require_relative "./active_support_transaction_subscriber"
|
8
|
+
Isolator::ActiveSupportTransactionSubscriber.subscribe!
|
9
|
+
else
|
10
|
+
Isolator::ActiveSupportSubscriber.subscribe!("sql.active_record")
|
11
|
+
end
|
@@ -7,12 +7,46 @@ module Isolator
|
|
7
7
|
START_PATTERN = %r{(\ABEGIN|\ASAVEPOINT)}xi
|
8
8
|
FINISH_PATTERN = %r{(\ACOMMIT|\AROLLBACK|\ARELEASE|\AEND TRANSACTION)}xi
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
class Subscriber
|
11
|
+
def start(event, id, payload)
|
12
|
+
return unless start_event?(payload[:sql])
|
13
|
+
|
14
|
+
connection_id = extract_connection_id(payload)
|
15
|
+
|
16
|
+
Isolator.incr_transactions!(connection_id)
|
17
|
+
end
|
18
|
+
|
19
|
+
def finish(event, id, payload)
|
20
|
+
return unless finish_event?(payload[:sql])
|
21
|
+
|
22
|
+
connection_id = extract_connection_id(payload)
|
23
|
+
|
24
|
+
Isolator.decr_transactions!(connection_id)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def start_event?(sql)
|
30
|
+
START_PATTERN.match?(sanitize_query(sql))
|
15
31
|
end
|
32
|
+
|
33
|
+
def finish_event?(sql)
|
34
|
+
FINISH_PATTERN.match?(sanitize_query(sql))
|
35
|
+
end
|
36
|
+
|
37
|
+
# Prevents "ArgumentError: invalid byte sequence in UTF-8" by replacing invalid byte sequence with "?"
|
38
|
+
def sanitize_query(sql)
|
39
|
+
sql.encode("UTF-8", "binary", invalid: :replace, undef: :replace, replace: "?")
|
40
|
+
end
|
41
|
+
|
42
|
+
def extract_connection_id(payload)
|
43
|
+
payload[:connection_id] || payload[:connection]&.object_id || 0
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.subscribe!(event)
|
48
|
+
subscriber = Subscriber.new
|
49
|
+
::ActiveSupport::Notifications.subscribe(event, subscriber)
|
16
50
|
end
|
17
51
|
end
|
18
52
|
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Isolator
|
4
|
+
# ActiveSupport notifications subscriber for "transaction.active_record" event (new in Rails 7.1)
|
5
|
+
module ActiveSupportTransactionSubscriber
|
6
|
+
class Subscriber < ActiveSupportSubscriber::Subscriber
|
7
|
+
attr_reader :stacks
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@stacks = Hash.new { |h, k| h[k] = [] }
|
11
|
+
end
|
12
|
+
|
13
|
+
def start(event, id, payload)
|
14
|
+
if event.start_with?("transaction.")
|
15
|
+
connection_id = extract_transaction_connection_id(payload)
|
16
|
+
|
17
|
+
# transaction.active_record can be issued without a query (when we restart the transaction),
|
18
|
+
# so we should add a new one on the stack.
|
19
|
+
# Example: https://github.com/rails/rails/blob/ce49fa9b31cd4a21d43db39d0cea364bce28b51d/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb#L337
|
20
|
+
if stacks[connection_id].last == :raw
|
21
|
+
# Update the type of the last transaction event
|
22
|
+
stacks[connection_id].pop
|
23
|
+
stacks[connection_id] << :transaction
|
24
|
+
else
|
25
|
+
stacks[connection_id] << :transaction
|
26
|
+
Isolator.incr_transactions!(connection_id)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def finish(event, id, payload)
|
32
|
+
if event.start_with?("sql.")
|
33
|
+
if start_event?(payload[:sql])
|
34
|
+
connection_id = extract_connection_id(payload)
|
35
|
+
|
36
|
+
stacks[connection_id] << :raw
|
37
|
+
|
38
|
+
Isolator.incr_transactions!(connection_id)
|
39
|
+
end
|
40
|
+
|
41
|
+
if finish_event?(payload[:sql])
|
42
|
+
connection_id = extract_connection_id(payload)
|
43
|
+
|
44
|
+
# Decrement only if the transaction was started in the raw mode,
|
45
|
+
# otherwise we should wait for the "transaction" event
|
46
|
+
if stacks[connection_id].last == :raw
|
47
|
+
stacks[connection_id].pop
|
48
|
+
Isolator.decr_transactions!(connection_id)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
if event.start_with?("transaction.")
|
54
|
+
connection_id = extract_transaction_connection_id(payload)
|
55
|
+
stacks[connection_id].pop
|
56
|
+
|
57
|
+
Isolator.decr_transactions!(connection_id)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def extract_transaction_connection_id(payload)
|
64
|
+
payload[:connection]&.object_id || 0
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.subscribe!(event = "transaction.active_record", sql_event = "sql.active_record")
|
69
|
+
subscriber = Subscriber.new
|
70
|
+
::ActiveSupport::Notifications.subscribe(event, subscriber)
|
71
|
+
::ActiveSupport::Notifications.subscribe(sql_event, subscriber)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/isolator/version.rb
CHANGED
data/lib/isolator.rb
CHANGED
@@ -198,15 +198,23 @@ module Isolator
|
|
198
198
|
|
199
199
|
def debug!(msg)
|
200
200
|
return unless debug_enabled
|
201
|
-
msg = "[ISOLATOR DEBUG] #{msg}"
|
202
201
|
|
203
|
-
|
204
|
-
source = extract_source_location(caller)
|
202
|
+
separator = " ↳ "
|
205
203
|
|
206
|
-
|
204
|
+
begin
|
205
|
+
msg = "[ISOLATOR DEBUG] #{msg}"
|
206
|
+
if backtrace_cleaner && backtrace_length.positive?
|
207
|
+
source = extract_source_location(caller)
|
208
|
+
|
209
|
+
msg = "#{msg}\n #{separator}#{source.join("\n")}" unless source.empty?
|
210
|
+
end
|
211
|
+
|
212
|
+
$stdout.puts(colorize_debug(msg))
|
213
|
+
rescue Encoding::CompatibilityError
|
214
|
+
should_retry = separator != " - "
|
215
|
+
separator = " - "
|
216
|
+
retry if should_retry
|
207
217
|
end
|
208
|
-
|
209
|
-
$stdout.puts(colorize_debug(msg))
|
210
218
|
end
|
211
219
|
|
212
220
|
def extract_source_location(locations)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: isolator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vladimir Dementyev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-09-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sniffer
|
@@ -313,6 +313,7 @@ files:
|
|
313
313
|
- lib/isolator/orm_adapters.rb
|
314
314
|
- lib/isolator/orm_adapters/active_record.rb
|
315
315
|
- lib/isolator/orm_adapters/active_support_subscriber.rb
|
316
|
+
- lib/isolator/orm_adapters/active_support_transaction_subscriber.rb
|
316
317
|
- lib/isolator/orm_adapters/rom_active_support.rb
|
317
318
|
- lib/isolator/railtie.rb
|
318
319
|
- lib/isolator/simple_hashie.rb
|