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 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.