advanced_connection 0.5.6 → 0.5.7
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/.editorconfig +15 -0
- data/Gemfile.lock +1 -1
- data/README.md +140 -0
- data/lib/advanced_connection.rb +2 -2
- data/lib/advanced_connection/active_record_ext/abstract_adapter/statement_pooling.rb +2 -2
- data/lib/advanced_connection/active_record_ext/connection_pool/idle_manager.rb +25 -2
- data/lib/advanced_connection/config.rb +26 -16
- data/lib/advanced_connection/railtie.rb +11 -2
- data/lib/advanced_connection/version.rb +1 -1
- metadata +4 -3
- data/README.rdoc +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40fc0d030c3afc87fe6de323b95e05ee08d7b194
|
4
|
+
data.tar.gz: 53e478d0b9f79a4c419988b1cf68eee4d644830b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6276f4f159d3bae45e449e500bebad258d89058b871e558eacfa1ad04ab32ca8b9ccf063f4367155f454f5ad0f047591bb986110e8488628d984df8e2f5d695
|
7
|
+
data.tar.gz: 6c437a28543af4e8a0bd4a5718435e06d9a91a5f74e8ec11775b86c33d187f593be9d340899d41272f1dabd7821f456d5247189025a9f498f4db248b48cb6bb9
|
data/.editorconfig
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
root = true
|
2
|
+
|
3
|
+
[*]
|
4
|
+
indent_style = space
|
5
|
+
indent_size = 2
|
6
|
+
end_of_line = lf
|
7
|
+
charset = utf-8
|
8
|
+
trim_trailing_whitespace = true
|
9
|
+
insert_final_newline = true
|
10
|
+
|
11
|
+
[*.md]
|
12
|
+
trim_trailing_whitespace = false
|
13
|
+
|
14
|
+
[*.rdoc]
|
15
|
+
trim_trailing_whitespace = false
|
data/Gemfile.lock
CHANGED
data/README.md
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
# Advanced Connection
|
2
|
+
|
3
|
+
AdvancedConnection is a rails (~> 4.1) plugin that provides advanced management features for Rails' ActiveRecord connection pool. Features include:
|
4
|
+
- Idle Connection Manager
|
5
|
+
- Connection-less Code Blocks
|
6
|
+
- Statement Pooling (**EXPERIMENTAL**)
|
7
|
+
|
8
|
+
### Version
|
9
|
+
0.5.6
|
10
|
+
|
11
|
+
### Installation
|
12
|
+
|
13
|
+
You need Gulp installed globally:
|
14
|
+
|
15
|
+
```sh
|
16
|
+
$ gem install advanced_connection
|
17
|
+
```
|
18
|
+
|
19
|
+
or in your Gemfile, add:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
gem 'advanced_connection', '~> 0.5.6'
|
23
|
+
```
|
24
|
+
|
25
|
+
Then generate your `AdvancedConnection` configuration by executing:
|
26
|
+
|
27
|
+
```sh
|
28
|
+
bundle exec rails generate advanced_connection:install
|
29
|
+
```
|
30
|
+
|
31
|
+
### Usage and Configuration
|
32
|
+
|
33
|
+
#### Idle Connection Manager
|
34
|
+
|
35
|
+
Enabling this will enable idle connection management. This allows you to specify settings
|
36
|
+
to enable automatic warmup of connections on rails startup, min/max idle connections and
|
37
|
+
idle connection culling.
|
38
|
+
|
39
|
+
```text
|
40
|
+
enable_idle_connection_manager = true | false
|
41
|
+
```
|
42
|
+
|
43
|
+
Pool queue type determines both how free connections will be checkout out of the pool, as well as how idle connections will be culled. The options are:
|
44
|
+
|
45
|
+
<dl>
|
46
|
+
<dt><strong>:fifo</strong></dt>
|
47
|
+
<dd>All connections will have an equal opportunity to be used and culled (default)</dd>
|
48
|
+
<br />
|
49
|
+
<dt><strong>:lifo, :stack</strong></dt>
|
50
|
+
<dd>More frequently used connections will be reused, leaving less frequently used connections to be culled</dd>
|
51
|
+
<br />
|
52
|
+
<dt><strong>:prefer_older</strong></dt>
|
53
|
+
<dd>Longer lived connections will tend to stick around longer, with younger connections being culled</dd>
|
54
|
+
<br />
|
55
|
+
<dt><strong>:prefer_younger</strong></dt>
|
56
|
+
<dd>Younger lived connections will tend to stick around longer, with older connections being culled</dd>
|
57
|
+
</dl>
|
58
|
+
|
59
|
+
```text
|
60
|
+
connection_pool_queue_type = :fifo | :lifo | :stack | :prefer_older | :prefer_younger
|
61
|
+
```
|
62
|
+
|
63
|
+
How many connections to prestart on initial startup of rails. This can help to reduce the time it takes a restarted production node to start responding again.
|
64
|
+
```text
|
65
|
+
warmup_connections = integer | false
|
66
|
+
```
|
67
|
+
|
68
|
+
Minimum number of connection to keep idle. If, during the idle check, you have fewer than this many connections idle, then a number of new connections will be created up to this this number.
|
69
|
+
```text
|
70
|
+
min_idle_connections = integer
|
71
|
+
```
|
72
|
+
|
73
|
+
Maximum number of connections that can remain idle without being culled. If you have
|
74
|
+
more idle conections than this, only the difference between the total idle and this
|
75
|
+
maximum will be culled.
|
76
|
+
```text
|
77
|
+
max_idle_connections = integer | Float::INFINITY
|
78
|
+
```
|
79
|
+
|
80
|
+
How long (in seconds) a connection can remain idle before being culled
|
81
|
+
```text
|
82
|
+
max_idle_time = integer
|
83
|
+
```
|
84
|
+
|
85
|
+
How many seconds between idle checks (defaults to max_idle_time)
|
86
|
+
```text
|
87
|
+
idle_check_interval = integer
|
88
|
+
```
|
89
|
+
|
90
|
+
#### Connection-less Code Blocks
|
91
|
+
|
92
|
+
Enabling this will add a new method to ActiveRecord::Base that allows you to mark a block of code as not requiring a connection. This can be useful in reducing pressure on the pool, especially when you have sections of code that make potentially long-lived external requests. E.g.,
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
require 'open-uri'
|
96
|
+
results = ActiveRecord::Base.without_connection do
|
97
|
+
open('http://some-slow-site.com/api/')
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
During the call to the remote site, the db connection is checked in and subsequently checked back out once the block finishes. To enable this feature, uncomment the following:
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
enable_without_connection = true | false
|
105
|
+
```
|
106
|
+
|
107
|
+
<div style="color: rgb(201, 79, 79); padding-bottom: 1em;"> WARNING: this feature cannot be enabled with Statement Pooling.</div>
|
108
|
+
|
109
|
+
Additionally, you can hook into the checkin / checkout lifecycle by way of callbacks. This can be extremely useful when employing something like [`Apartment`][apt] to manage switching between tenants.
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
without_connection_callbacks = {
|
113
|
+
# runs right before the connection is checked back into the pool
|
114
|
+
before: ->() { },
|
115
|
+
around: ->(&block) {
|
116
|
+
tenant = Apartment::Tenant.current
|
117
|
+
block.call
|
118
|
+
Apartment::Tenant.switch(tenant)
|
119
|
+
},
|
120
|
+
# runs right after the connection is checked back out of the pool
|
121
|
+
after: ->() { }
|
122
|
+
}
|
123
|
+
```
|
124
|
+
|
125
|
+
#### Statement Pooling
|
126
|
+
|
127
|
+
### Todos
|
128
|
+
|
129
|
+
- Finish development of Statement Pooling
|
130
|
+
- Write Tests
|
131
|
+
- Add Code Comments
|
132
|
+
|
133
|
+
License
|
134
|
+
----
|
135
|
+
|
136
|
+
MIT
|
137
|
+
|
138
|
+
|
139
|
+
[//]: # (references)
|
140
|
+
[apt]: <https://github.com/influitive/apartment/>
|
data/lib/advanced_connection.rb
CHANGED
@@ -28,7 +28,7 @@ module AdvancedConnection::ActiveRecordExt
|
|
28
28
|
|
29
29
|
module ExecuteWrapper
|
30
30
|
def __wrap_adapter_exec_methods(*methods)
|
31
|
-
Array(methods).
|
31
|
+
Array(methods).flat_map(&:to_sym).each do |exec_method|
|
32
32
|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
33
33
|
def #{exec_method}_with_callback(sql, *args, &block)
|
34
34
|
if Thread.current[:without_callbacks] || sql =~ /^BEGIN/i || transaction_open? || pool.nil?
|
@@ -49,7 +49,7 @@ module AdvancedConnection::ActiveRecordExt
|
|
49
49
|
alias_method :__wrap_adapter_exec_method, :__wrap_adapter_exec_methods
|
50
50
|
|
51
51
|
def __wrap_without_callbacks(*methods)
|
52
|
-
Array(methods).
|
52
|
+
Array(methods).flat_map(&:to_sym).each do |exec_method|
|
53
53
|
target, punctuation = exec_method.to_s.sub(/([?!=])$/, ''), $1
|
54
54
|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
55
55
|
def #{target}_with_no_callbacks#{punctuation}(*args, &block)
|
@@ -28,8 +28,12 @@ module AdvancedConnection::ActiveRecordExt
|
|
28
28
|
alias_method_chain :initialize, :advanced_connection
|
29
29
|
alias_method_chain :checkin, :last_checked_in
|
30
30
|
|
31
|
+
attr_reader :idle_manager
|
32
|
+
|
31
33
|
class IdleManager
|
34
|
+
attr_accessor :interval
|
32
35
|
attr_reader :thread
|
36
|
+
private :thread
|
33
37
|
|
34
38
|
def initialize(pool, interval)
|
35
39
|
@pool = pool
|
@@ -37,7 +41,25 @@ module AdvancedConnection::ActiveRecordExt
|
|
37
41
|
@thread = nil
|
38
42
|
end
|
39
43
|
|
40
|
-
def
|
44
|
+
def status
|
45
|
+
if @thread
|
46
|
+
@thread.alive? ? :running : :dead
|
47
|
+
else
|
48
|
+
:stopped
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def restart
|
53
|
+
stop.start
|
54
|
+
end
|
55
|
+
|
56
|
+
def stop
|
57
|
+
@thread.kill if @thread.alive?
|
58
|
+
@thread = nil
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
def start
|
41
63
|
return unless @interval > 0
|
42
64
|
|
43
65
|
@thread ||= Thread.new(@pool, @interval) { |pool, interval|
|
@@ -60,6 +82,7 @@ module AdvancedConnection::ActiveRecordExt
|
|
60
82
|
end
|
61
83
|
end
|
62
84
|
}
|
85
|
+
self
|
63
86
|
end
|
64
87
|
end
|
65
88
|
end
|
@@ -76,7 +99,7 @@ module AdvancedConnection::ActiveRecordExt
|
|
76
99
|
Queues::FIFO.new
|
77
100
|
end
|
78
101
|
|
79
|
-
@idle_manager = IdleManager.new(self, idle_check_interval).tap(&:
|
102
|
+
@idle_manager = IdleManager.new(self, idle_check_interval).tap(&:start)
|
80
103
|
end
|
81
104
|
|
82
105
|
def queue_type
|
@@ -20,6 +20,7 @@
|
|
20
20
|
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
21
|
#
|
22
22
|
require 'singleton'
|
23
|
+
require 'monitor'
|
23
24
|
|
24
25
|
module AdvancedConnection
|
25
26
|
class Config
|
@@ -99,17 +100,24 @@ module AdvancedConnection
|
|
99
100
|
def initialize
|
100
101
|
@loaded = false
|
101
102
|
@config = DEFAULT_CONFIG.deep_dup
|
103
|
+
@mutex = Monitor.new
|
102
104
|
end
|
103
105
|
|
106
|
+
def synchronize
|
107
|
+
@mutex.synchronize { yield }
|
108
|
+
end
|
109
|
+
private :synchronize
|
110
|
+
|
104
111
|
def can_enable?
|
105
|
-
# don't enable if we're running rake tasks,
|
112
|
+
# don't enable if we're running rake tasks generally,
|
113
|
+
# and specifically, if it's db: or assets: tasks
|
106
114
|
return false if $0.include? 'rake'
|
107
115
|
return false if ARGV.grep(/^(assets|db):/).any?
|
108
116
|
true
|
109
117
|
end
|
110
118
|
|
111
119
|
def loaded!
|
112
|
-
@loaded = true
|
120
|
+
synchronize { @loaded = true }
|
113
121
|
end
|
114
122
|
|
115
123
|
def [](key)
|
@@ -140,7 +148,7 @@ module AdvancedConnection
|
|
140
148
|
if enable_statement_pooling && !!value
|
141
149
|
raise Error::ConfigError, "WithoutConnection blocks conflict with Statement Pooling feature"
|
142
150
|
end
|
143
|
-
@config[:enable_without_connection] = !!value
|
151
|
+
synchronize { @config[:enable_without_connection] = !!value }
|
144
152
|
end
|
145
153
|
|
146
154
|
def enable_statement_pooling
|
@@ -151,7 +159,7 @@ module AdvancedConnection
|
|
151
159
|
if enable_without_connection && !!value
|
152
160
|
raise Error::ConfigError, "Statement Pooling conflicts with WithoutConnection feature"
|
153
161
|
end
|
154
|
-
@config[:enable_statement_pooling] = !!value
|
162
|
+
synchronize { @config[:enable_statement_pooling] = !!value }
|
155
163
|
end
|
156
164
|
|
157
165
|
def enable_idle_connection_manager
|
@@ -159,7 +167,7 @@ module AdvancedConnection
|
|
159
167
|
end
|
160
168
|
|
161
169
|
def enable_idle_connection_manager=(value)
|
162
|
-
@config[:enable_idle_connection_manager] = !!value
|
170
|
+
synchronize { @config[:enable_idle_connection_manager] = !!value }
|
163
171
|
end
|
164
172
|
|
165
173
|
def warmup_connections
|
@@ -172,7 +180,7 @@ module AdvancedConnection
|
|
172
180
|
"or a valid positive integer, but found `#{value.inspect}`"
|
173
181
|
end
|
174
182
|
|
175
|
-
@config[:warmup_connections] = value.to_s =~ /^\d+$/ ? value.to_i : false
|
183
|
+
synchronize { @config[:warmup_connections] = value.to_s =~ /^\d+$/ ? value.to_i : false }
|
176
184
|
end
|
177
185
|
|
178
186
|
def min_idle_connections
|
@@ -184,7 +192,7 @@ module AdvancedConnection
|
|
184
192
|
fail Error::ConfigError, 'Expected min_idle_connections to be ' \
|
185
193
|
"a valid integer value, but found `#{value.inspect}`"
|
186
194
|
end
|
187
|
-
@config[:min_idle_connections] = value.to_i
|
195
|
+
synchronize { @config[:min_idle_connections] = value.to_i }
|
188
196
|
end
|
189
197
|
|
190
198
|
def max_idle_connections
|
@@ -196,12 +204,14 @@ module AdvancedConnection
|
|
196
204
|
fail Error::ConfigError, 'Expected max_idle_connections to be ' \
|
197
205
|
"a valid integer value, but found `#{value.inspect}`"
|
198
206
|
end
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
207
|
+
synchronize {
|
208
|
+
@config[:max_idle_connections] = begin
|
209
|
+
value.to_i
|
210
|
+
rescue FloatDomainError
|
211
|
+
raise unless $!.message =~ /infinity/i
|
212
|
+
::Float::INFINITY
|
213
|
+
end
|
214
|
+
}
|
205
215
|
end
|
206
216
|
|
207
217
|
def max_idle_time
|
@@ -213,7 +223,7 @@ module AdvancedConnection
|
|
213
223
|
fail Error::ConfigError, 'Expected max_idle_time to be ' \
|
214
224
|
"a valid integer value, but found `#{value.inspect}`"
|
215
225
|
end
|
216
|
-
@config[:max_idle_time] = value.to_i
|
226
|
+
synchronize { @config[:max_idle_time] = value.to_i }
|
217
227
|
end
|
218
228
|
|
219
229
|
def idle_check_interval
|
@@ -225,7 +235,7 @@ module AdvancedConnection
|
|
225
235
|
fail Error::ConfigError, 'Expected idle_check_interval to be ' \
|
226
236
|
"a valid integer value, but found `#{value.inspect}`"
|
227
237
|
end
|
228
|
-
@config[:idle_check_interval] = value.to_i
|
238
|
+
synchronize { @config[:idle_check_interval] = value.to_i }
|
229
239
|
end
|
230
240
|
|
231
241
|
def connection_pool_queue_type
|
@@ -243,7 +253,7 @@ module AdvancedConnection
|
|
243
253
|
':fifo, :lifo, :stack, :prefer_younger, or :prefer_older ' \
|
244
254
|
"but found `#{value.inspect}`"
|
245
255
|
end
|
246
|
-
@config[:connection_pool_queue_type] = value
|
256
|
+
synchronize { @config[:connection_pool_queue_type] = value }
|
247
257
|
end
|
248
258
|
end
|
249
259
|
end
|
@@ -25,11 +25,20 @@ module AdvancedConnection
|
|
25
25
|
|
26
26
|
if AdvancedConnection.can_enable?
|
27
27
|
ActiveSupport.on_load(:before_initialize) do
|
28
|
-
ActiveSupport.on_load(:active_record)
|
28
|
+
ActiveSupport.on_load(:active_record) {
|
29
29
|
# load our intitializer ASAP so we can make use of user defined configuration
|
30
30
|
load Rails.root.join('config', 'initializers', 'advanced_connection.rb')
|
31
|
+
|
32
|
+
# now override the settings with environment speicific ones
|
33
|
+
AdvancedConnection.configure(true) do |ac_cfg|
|
34
|
+
Rails.configuration.advanced_connection.each { |option, value|
|
35
|
+
ac_cfg[option] = value
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
# and finally, compose ourself
|
31
40
|
ActiveRecord::Base.send(:include, AdvancedConnection::ActiveRecordExt)
|
32
|
-
|
41
|
+
}
|
33
42
|
end
|
34
43
|
|
35
44
|
config.after_initialize do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: advanced_connection
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Carl P. Corliss
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-03-
|
11
|
+
date: 2016-03-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -215,6 +215,7 @@ extensions: []
|
|
215
215
|
extra_rdoc_files: []
|
216
216
|
files:
|
217
217
|
- ".document"
|
218
|
+
- ".editorconfig"
|
218
219
|
- ".gitignore"
|
219
220
|
- ".rspec"
|
220
221
|
- ".rubocop.yml"
|
@@ -222,7 +223,7 @@ files:
|
|
222
223
|
- Gemfile
|
223
224
|
- Gemfile.lock
|
224
225
|
- MIT-LICENSE
|
225
|
-
- README.
|
226
|
+
- README.md
|
226
227
|
- Rakefile
|
227
228
|
- advanced_connection.gemspec
|
228
229
|
- gemfiles/jruby/rails4_1.gemfile
|
data/README.rdoc
DELETED