activerecord-health 0.1.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 +7 -0
- data/CONTRIBUTING.md +39 -0
- data/LICENSE.txt +21 -0
- data/README.md +239 -0
- data/Rakefile +25 -0
- data/docker-compose.yml +27 -0
- data/lib/activerecord/health/adapters/mysql_adapter.rb +60 -0
- data/lib/activerecord/health/adapters/postgresql_adapter.rb +23 -0
- data/lib/activerecord/health/configuration.rb +61 -0
- data/lib/activerecord/health/extensions.rb +44 -0
- data/lib/activerecord/health/railtie.rb +11 -0
- data/lib/activerecord/health/version.rb +7 -0
- data/lib/activerecord/health.rb +134 -0
- data/lib/activerecord-health.rb +3 -0
- data/test/integration/mysql_integration_test.rb +104 -0
- data/test/integration/postgresql_integration_test.rb +104 -0
- data/test/test_helper.rb +74 -0
- data/test/unit/adapters/mysql_adapter_test.rb +94 -0
- data/test/unit/adapters/postgresql_adapter_test.rb +25 -0
- data/test/unit/configuration_test.rb +140 -0
- data/test/unit/extensions_test.rb +113 -0
- data/test/unit/health_test.rb +189 -0
- data/test/unit/sheddable_test.rb +92 -0
- metadata +94 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
require "activerecord/health/extensions"
|
|
5
|
+
|
|
6
|
+
class ConnectionExtensionTest < ActiveRecord::Health::TestCase
|
|
7
|
+
def test_healthy_returns_true_when_below_threshold
|
|
8
|
+
cache = MockCache.new
|
|
9
|
+
cache.write("activerecord_health:load_pct:primary", 0.5)
|
|
10
|
+
|
|
11
|
+
ActiveRecord::Health.configure do |config|
|
|
12
|
+
config.vcpu_count = 16
|
|
13
|
+
config.threshold = 0.75
|
|
14
|
+
config.cache = cache
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
connection = MockConnectionWithExtension.new("primary")
|
|
18
|
+
assert connection.healthy?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_healthy_returns_false_when_above_threshold
|
|
22
|
+
cache = MockCache.new
|
|
23
|
+
cache.write("activerecord_health:load_pct:primary", 0.9)
|
|
24
|
+
|
|
25
|
+
ActiveRecord::Health.configure do |config|
|
|
26
|
+
config.vcpu_count = 16
|
|
27
|
+
config.threshold = 0.75
|
|
28
|
+
config.cache = cache
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
connection = MockConnectionWithExtension.new("primary")
|
|
32
|
+
refute connection.healthy?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_load_pct_returns_cached_value
|
|
36
|
+
cache = MockCache.new
|
|
37
|
+
cache.write("activerecord_health:load_pct:primary", 0.625)
|
|
38
|
+
|
|
39
|
+
ActiveRecord::Health.configure do |config|
|
|
40
|
+
config.vcpu_count = 16
|
|
41
|
+
config.cache = cache
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
connection = MockConnectionWithExtension.new("primary")
|
|
45
|
+
assert_equal 0.625, connection.load_pct
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class ModelExtensionTest < ActiveRecord::Health::TestCase
|
|
50
|
+
def test_database_healthy_returns_true_when_below_threshold
|
|
51
|
+
cache = MockCache.new
|
|
52
|
+
cache.write("activerecord_health:load_pct:primary", 0.5)
|
|
53
|
+
|
|
54
|
+
ActiveRecord::Health.configure do |config|
|
|
55
|
+
config.vcpu_count = 16
|
|
56
|
+
config.threshold = 0.75
|
|
57
|
+
config.cache = cache
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
assert MockModelWithExtension.database_healthy?
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def test_database_healthy_returns_false_when_above_threshold
|
|
64
|
+
cache = MockCache.new
|
|
65
|
+
cache.write("activerecord_health:load_pct:primary", 0.9)
|
|
66
|
+
|
|
67
|
+
ActiveRecord::Health.configure do |config|
|
|
68
|
+
config.vcpu_count = 16
|
|
69
|
+
config.threshold = 0.75
|
|
70
|
+
config.cache = cache
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
refute MockModelWithExtension.database_healthy?
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
MockExtensionDbConfig = Struct.new(:name)
|
|
78
|
+
|
|
79
|
+
class MockConnectionWithExtension
|
|
80
|
+
include ActiveRecord::Health::ConnectionExtension
|
|
81
|
+
|
|
82
|
+
def initialize(db_config_name)
|
|
83
|
+
@db_config_name = db_config_name
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def adapter_name
|
|
87
|
+
"PostgreSQL"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def select_value(query)
|
|
91
|
+
0
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def pool
|
|
95
|
+
self
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def db_config
|
|
99
|
+
MockExtensionDbConfig.new(@db_config_name)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
class MockModelWithExtension
|
|
104
|
+
extend ActiveRecord::Health::ModelExtension
|
|
105
|
+
|
|
106
|
+
def self.connection_db_config
|
|
107
|
+
MockExtensionDbConfig.new("primary")
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def self.connection
|
|
111
|
+
MockConnectionWithExtension.new("primary")
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class HealthTest < ActiveRecord::Health::TestCase
|
|
6
|
+
def test_ok_returns_true_when_load_below_threshold
|
|
7
|
+
cache = MockCache.new
|
|
8
|
+
cache.write("activerecord_health:load_pct:primary", 0.5)
|
|
9
|
+
|
|
10
|
+
ActiveRecord::Health.configure do |config|
|
|
11
|
+
config.vcpu_count = 16
|
|
12
|
+
config.threshold = 0.75
|
|
13
|
+
config.cache = cache
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
mock_model = MockModel.new("primary")
|
|
17
|
+
assert ActiveRecord::Health.ok?(model: mock_model)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def test_ok_returns_false_when_load_above_threshold
|
|
21
|
+
cache = MockCache.new
|
|
22
|
+
cache.write("activerecord_health:load_pct:primary", 0.9)
|
|
23
|
+
|
|
24
|
+
ActiveRecord::Health.configure do |config|
|
|
25
|
+
config.vcpu_count = 16
|
|
26
|
+
config.threshold = 0.75
|
|
27
|
+
config.cache = cache
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
mock_model = MockModel.new("primary")
|
|
31
|
+
refute ActiveRecord::Health.ok?(model: mock_model)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_ok_returns_true_when_load_equals_threshold
|
|
35
|
+
cache = MockCache.new
|
|
36
|
+
cache.write("activerecord_health:load_pct:primary", 0.75)
|
|
37
|
+
|
|
38
|
+
ActiveRecord::Health.configure do |config|
|
|
39
|
+
config.vcpu_count = 16
|
|
40
|
+
config.threshold = 0.75
|
|
41
|
+
config.cache = cache
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
mock_model = MockModel.new("primary")
|
|
45
|
+
assert ActiveRecord::Health.ok?(model: mock_model)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_ok_returns_true_when_cache_fails
|
|
49
|
+
ActiveRecord::Health.configure do |config|
|
|
50
|
+
config.vcpu_count = 16
|
|
51
|
+
config.cache = FailingCache.new
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
mock_model = MockModel.new("primary")
|
|
55
|
+
assert ActiveRecord::Health.ok?(model: mock_model)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def test_load_pct_returns_cached_value
|
|
59
|
+
cache = MockCache.new
|
|
60
|
+
cache.write("activerecord_health:load_pct:primary", 0.625)
|
|
61
|
+
|
|
62
|
+
ActiveRecord::Health.configure do |config|
|
|
63
|
+
config.vcpu_count = 16
|
|
64
|
+
config.cache = cache
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
mock_model = MockModel.new("primary")
|
|
68
|
+
assert_equal 0.625, ActiveRecord::Health.load_pct(model: mock_model)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def test_load_pct_returns_zero_when_cache_fails
|
|
72
|
+
ActiveRecord::Health.configure do |config|
|
|
73
|
+
config.vcpu_count = 16
|
|
74
|
+
config.cache = FailingCache.new
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
mock_model = MockModel.new("primary")
|
|
78
|
+
assert_equal 0.0, ActiveRecord::Health.load_pct(model: mock_model)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def test_load_pct_queries_database_when_not_cached
|
|
82
|
+
cache = MockCache.new
|
|
83
|
+
|
|
84
|
+
ActiveRecord::Health.configure do |config|
|
|
85
|
+
config.vcpu_count = 16
|
|
86
|
+
config.cache = cache
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
connection = MockConnection.new(active_session_count: 8)
|
|
90
|
+
mock_model = MockModel.new("primary", connection)
|
|
91
|
+
|
|
92
|
+
assert_equal 0.5, ActiveRecord::Health.load_pct(model: mock_model)
|
|
93
|
+
assert_equal 0.5, cache.read("activerecord_health:load_pct:primary")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def test_load_pct_returns_1_0_when_database_query_fails
|
|
97
|
+
cache = MockCache.new
|
|
98
|
+
|
|
99
|
+
ActiveRecord::Health.configure do |config|
|
|
100
|
+
config.vcpu_count = 16
|
|
101
|
+
config.cache = cache
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
connection = MockConnection.new(should_fail: true)
|
|
105
|
+
mock_model = MockModel.new("primary", connection)
|
|
106
|
+
|
|
107
|
+
assert_equal 1.0, ActiveRecord::Health.load_pct(model: mock_model)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def test_ok_returns_false_when_database_query_fails
|
|
111
|
+
cache = MockCache.new
|
|
112
|
+
|
|
113
|
+
ActiveRecord::Health.configure do |config|
|
|
114
|
+
config.vcpu_count = 16
|
|
115
|
+
config.threshold = 0.75
|
|
116
|
+
config.cache = cache
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
connection = MockConnection.new(should_fail: true)
|
|
120
|
+
mock_model = MockModel.new("primary", connection)
|
|
121
|
+
|
|
122
|
+
refute ActiveRecord::Health.ok?(model: mock_model)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def test_uses_per_model_configuration
|
|
126
|
+
cache = MockCache.new
|
|
127
|
+
cache.write("activerecord_health:load_pct:animals", 0.6)
|
|
128
|
+
|
|
129
|
+
ActiveRecord::Health.configure do |config|
|
|
130
|
+
config.vcpu_count = 16
|
|
131
|
+
config.threshold = 0.75
|
|
132
|
+
config.cache = cache
|
|
133
|
+
|
|
134
|
+
config.for_model(AnimalsRecord) do |db|
|
|
135
|
+
db.vcpu_count = 8
|
|
136
|
+
db.threshold = 0.5
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
mock_model = MockModel.new("animals")
|
|
141
|
+
mock_model.define_singleton_method(:class) { AnimalsRecord }
|
|
142
|
+
|
|
143
|
+
refute ActiveRecord::Health.ok?(model: mock_model)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
MockDbConfig = Struct.new(:name)
|
|
148
|
+
|
|
149
|
+
class MockModel
|
|
150
|
+
attr_reader :connection
|
|
151
|
+
|
|
152
|
+
def initialize(db_config_name, connection = nil)
|
|
153
|
+
@db_config_name = db_config_name
|
|
154
|
+
@connection = connection || MockConnection.new
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def connection_db_config
|
|
158
|
+
MockDbConfig.new(@db_config_name)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def class
|
|
162
|
+
ActiveRecord::Base
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
class MockConnection
|
|
167
|
+
def initialize(active_session_count: 0, should_fail: false)
|
|
168
|
+
@active_session_count = active_session_count
|
|
169
|
+
@should_fail = should_fail
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def adapter_name
|
|
173
|
+
"PostgreSQL"
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def select_value(query)
|
|
177
|
+
raise "Connection failed" if @should_fail
|
|
178
|
+
@active_session_count
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def execute(query)
|
|
182
|
+
raise "Connection failed" if @should_fail
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def transaction
|
|
186
|
+
raise "Connection failed" if @should_fail
|
|
187
|
+
yield
|
|
188
|
+
end
|
|
189
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class SheddableTest < ActiveRecord::Health::TestCase
|
|
6
|
+
def test_sheddable_executes_block_and_returns_true_when_healthy
|
|
7
|
+
cache = MockCache.new
|
|
8
|
+
cache.write("activerecord_health:load_pct:primary", 0.5)
|
|
9
|
+
|
|
10
|
+
ActiveRecord::Health.configure do |config|
|
|
11
|
+
config.vcpu_count = 16
|
|
12
|
+
config.threshold = 0.75
|
|
13
|
+
config.cache = cache
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
mock_model = MockModel.new("primary")
|
|
17
|
+
executed = false
|
|
18
|
+
result = ActiveRecord::Health.sheddable(model: mock_model) { executed = true }
|
|
19
|
+
|
|
20
|
+
assert executed
|
|
21
|
+
assert result
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def test_sheddable_returns_false_and_skips_block_when_overloaded
|
|
25
|
+
cache = MockCache.new
|
|
26
|
+
cache.write("activerecord_health:load_pct:primary", 0.9)
|
|
27
|
+
|
|
28
|
+
ActiveRecord::Health.configure do |config|
|
|
29
|
+
config.vcpu_count = 16
|
|
30
|
+
config.threshold = 0.75
|
|
31
|
+
config.cache = cache
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
mock_model = MockModel.new("primary")
|
|
35
|
+
executed = false
|
|
36
|
+
result = ActiveRecord::Health.sheddable(model: mock_model) { executed = true }
|
|
37
|
+
|
|
38
|
+
refute executed
|
|
39
|
+
refute result
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def test_sheddable_pct_executes_block_and_returns_true_when_below_threshold
|
|
43
|
+
cache = MockCache.new
|
|
44
|
+
cache.write("activerecord_health:load_pct:primary", 0.4)
|
|
45
|
+
|
|
46
|
+
ActiveRecord::Health.configure do |config|
|
|
47
|
+
config.vcpu_count = 16
|
|
48
|
+
config.cache = cache
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
mock_model = MockModel.new("primary")
|
|
52
|
+
executed = false
|
|
53
|
+
result = ActiveRecord::Health.sheddable_pct(pct: 0.5, model: mock_model) { executed = true }
|
|
54
|
+
|
|
55
|
+
assert executed
|
|
56
|
+
assert result
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def test_sheddable_pct_returns_false_and_skips_block_when_above_threshold
|
|
60
|
+
cache = MockCache.new
|
|
61
|
+
cache.write("activerecord_health:load_pct:primary", 0.6)
|
|
62
|
+
|
|
63
|
+
ActiveRecord::Health.configure do |config|
|
|
64
|
+
config.vcpu_count = 16
|
|
65
|
+
config.cache = cache
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
mock_model = MockModel.new("primary")
|
|
69
|
+
executed = false
|
|
70
|
+
result = ActiveRecord::Health.sheddable_pct(pct: 0.5, model: mock_model) { executed = true }
|
|
71
|
+
|
|
72
|
+
refute executed
|
|
73
|
+
refute result
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def test_sheddable_pct_executes_block_and_returns_true_when_at_threshold
|
|
77
|
+
cache = MockCache.new
|
|
78
|
+
cache.write("activerecord_health:load_pct:primary", 0.5)
|
|
79
|
+
|
|
80
|
+
ActiveRecord::Health.configure do |config|
|
|
81
|
+
config.vcpu_count = 16
|
|
82
|
+
config.cache = cache
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
mock_model = MockModel.new("primary")
|
|
86
|
+
executed = false
|
|
87
|
+
result = ActiveRecord::Health.sheddable_pct(pct: 0.5, model: mock_model) { executed = true }
|
|
88
|
+
|
|
89
|
+
assert executed
|
|
90
|
+
assert result
|
|
91
|
+
end
|
|
92
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: activerecord-health
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Nate Berkopec
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: activerecord
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '7.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '7.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: activesupport
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '7.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '7.0'
|
|
40
|
+
description: A gem that checks database health by monitoring active session count
|
|
41
|
+
relative to available vCPUs. Intended for automatic load shedding.
|
|
42
|
+
email:
|
|
43
|
+
- nate.berkopec@speedshop.co
|
|
44
|
+
executables: []
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- CONTRIBUTING.md
|
|
49
|
+
- LICENSE.txt
|
|
50
|
+
- README.md
|
|
51
|
+
- Rakefile
|
|
52
|
+
- docker-compose.yml
|
|
53
|
+
- lib/activerecord-health.rb
|
|
54
|
+
- lib/activerecord/health.rb
|
|
55
|
+
- lib/activerecord/health/adapters/mysql_adapter.rb
|
|
56
|
+
- lib/activerecord/health/adapters/postgresql_adapter.rb
|
|
57
|
+
- lib/activerecord/health/configuration.rb
|
|
58
|
+
- lib/activerecord/health/extensions.rb
|
|
59
|
+
- lib/activerecord/health/railtie.rb
|
|
60
|
+
- lib/activerecord/health/version.rb
|
|
61
|
+
- test/integration/mysql_integration_test.rb
|
|
62
|
+
- test/integration/postgresql_integration_test.rb
|
|
63
|
+
- test/test_helper.rb
|
|
64
|
+
- test/unit/adapters/mysql_adapter_test.rb
|
|
65
|
+
- test/unit/adapters/postgresql_adapter_test.rb
|
|
66
|
+
- test/unit/configuration_test.rb
|
|
67
|
+
- test/unit/extensions_test.rb
|
|
68
|
+
- test/unit/health_test.rb
|
|
69
|
+
- test/unit/sheddable_test.rb
|
|
70
|
+
homepage: https://github.com/nateberkopec/activerecord-health
|
|
71
|
+
licenses:
|
|
72
|
+
- MIT
|
|
73
|
+
metadata:
|
|
74
|
+
allowed_push_host: https://rubygems.org
|
|
75
|
+
homepage_uri: https://github.com/nateberkopec/activerecord-health
|
|
76
|
+
source_code_uri: https://github.com/nateberkopec/activerecord-health
|
|
77
|
+
rdoc_options: []
|
|
78
|
+
require_paths:
|
|
79
|
+
- lib
|
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
81
|
+
requirements:
|
|
82
|
+
- - ">="
|
|
83
|
+
- !ruby/object:Gem::Version
|
|
84
|
+
version: 3.2.0
|
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
requirements: []
|
|
91
|
+
rubygems_version: 3.6.9
|
|
92
|
+
specification_version: 4
|
|
93
|
+
summary: Database health monitoring for ActiveRecord with automatic load shedding
|
|
94
|
+
test_files: []
|