advanced_connection 0.5.6 → 0.5.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5388815e17dd8184ac2da77dd66f12dc8f420c89
4
- data.tar.gz: 84e79b7bcaa1806c872f25831c0b688843af0bf8
3
+ metadata.gz: 40fc0d030c3afc87fe6de323b95e05ee08d7b194
4
+ data.tar.gz: 53e478d0b9f79a4c419988b1cf68eee4d644830b
5
5
  SHA512:
6
- metadata.gz: f1f7d22818b463df971f7dde6a17b4f19dcd35c5a048b88863684bf9dbe685e99c8c0a8fdbdd5a044681fd951e779ce0ad06df83dc9e84dab8b251906d8e5024
7
- data.tar.gz: c9a8fe419202cca4bce4d552c921cceb53d0e71b7b7ee861391172068d1c4670280d4bcf09ed69ce50500c3adce1369003ee01d2e22d9d4654701eae5de6adae
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- advanced_connection (0.5.6)
4
+ advanced_connection (0.5.7)
5
5
  activerecord (~> 4.1)
6
6
  activesupport (~> 4.1)
7
7
  rails (~> 4.1)
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/>
@@ -40,8 +40,8 @@ module AdvancedConnection
40
40
  config.to_h
41
41
  end
42
42
 
43
- def configure(overwrite = true)
44
- return unless overwrite
43
+ def configure(overwrite = false)
44
+ return config unless overwrite || !config.loaded?
45
45
  (yield config).tap { config.loaded! }
46
46
  end
47
47
 
@@ -28,7 +28,7 @@ module AdvancedConnection::ActiveRecordExt
28
28
 
29
29
  module ExecuteWrapper
30
30
  def __wrap_adapter_exec_methods(*methods)
31
- Array(methods).flatten.collect(&:to_sym).each do |exec_method|
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).flatten.collect(&:to_sym).each do |exec_method|
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 run
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(&:run)
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, in particular db: or assets: 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
- @config[:max_idle_connections] = begin
200
- value.to_i
201
- rescue FloatDomainError
202
- raise unless $!.message =~ /infinity/i
203
- ::Float::INFINITY
204
- end
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) do
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
- end
41
+ }
33
42
  end
34
43
 
35
44
  config.after_initialize do
@@ -22,7 +22,7 @@
22
22
  module AdvancedConnection
23
23
  MAJOR = 0
24
24
  MINOR = 5
25
- PATCH = 6
25
+ PATCH = 7
26
26
 
27
27
  VERSION = "%d.%d.%d" % [ MAJOR, MINOR, PATCH ]
28
28
  GEM_VERSION = Gem::Version.new(VERSION)
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.6
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-30 00:00:00.000000000 Z
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.rdoc
226
+ - README.md
226
227
  - Rakefile
227
228
  - advanced_connection.gemspec
228
229
  - gemfiles/jruby/rails4_1.gemfile
data/README.rdoc DELETED
@@ -1,3 +0,0 @@
1
- = AdvancedConnection
2
-
3
- This project rocks and uses MIT-LICENSE.