activerecord-bogacs 0.5.1 → 0.6.0

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
  SHA256:
3
- metadata.gz: 8d7d0cf56d16097d5faadc25e31228e1e6b6a1040703aa0fed0fd29a49d23622
4
- data.tar.gz: 7f00210ced14e5963220980fd749d0c8765d7555deab200c508daa44ca0770c7
3
+ metadata.gz: a11630875cf14ee4c1ea37721a04a1966eeb488874d23c833075e42c2e435852
4
+ data.tar.gz: 77016cc2628013c4a1791403fcbf65ad488f8db4f6a091a9659e1e0b523c7e38
5
5
  SHA512:
6
- metadata.gz: ebc49df8d9f33381b3134b4dec5b5765abb06bb69b353f21ce695e9f0c4869f9ef36ad4005d6a716d2f73bebfe986158cfc237dd477f6247594a57337adb7bef
7
- data.tar.gz: adc08a3fd5b9c6a2dde6daf9e75433c29fc7faf6c6832cd90e1fb8cf7f2be8a3df1e054486c13c22e8d5be1d4e3df74f4a42e27453d145b84a086e17f30efae4
6
+ metadata.gz: 89a482ca85813ef8b769355a0a42ffe0317024022c0da7ad337084029ad455976865199806b6f79fbd0b79c1288ec7f6a1654ec9aea66ec07b544b28329f5dcc
7
+ data.tar.gz: 624c0fe63b5ff31ef51abeb2dd537fe3ef2bddad81f03cd94df38fb7aafea3b93635a844554bb7597ec08fc551215c016e1c1ebcb3817230220455919db1ade3
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013-2017 [Karol Bucek](http://kares.org)
1
+ Copyright (c) 2013-2018 [Karol Bucek](http://kares.org)
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -6,7 +6,7 @@ ActiveRecord (all-year) pooling "alternatives" ... in a relaxed 'spa' fashion.
6
6
 
7
7
  Bogács is a village in Borsod-Abaúj-Zemplén county, Hungary.
8
8
 
9
- **WiP: do not put this on production if you do not understand the consequences!**
9
+ **NOTE: do not put this on production if you do not understand the consequences!**
10
10
 
11
11
  ## Install
12
12
 
@@ -22,8 +22,8 @@ add this line to your application's *Gemfile*:
22
22
 
23
23
  Bogacs' pools rely on a small monkey-patch that allows to change the AR pool.
24
24
  The desired pool class needs to be set before `establish_connection` happens,
25
- thus an initializer won't work, you might consider setting the pool class at
26
- the bottom of your *application.rb* e.g. :
25
+ thus an initializer (under Rails) won't work, you might consider setting the
26
+ pool class at the bottom of your *application.rb* e.g. :
27
27
 
28
28
  ```ruby
29
29
  Bundler.require(*Rails.groups)
@@ -34,19 +34,39 @@ module MyApp
34
34
  end
35
35
  end
36
36
 
37
- # sample AR-Bogacs setup using the "false" pool :
37
+ # sample AR-Bogacs setup using the "default" pool :
38
38
  if Rails.env.production?
39
- pool_class = ActiveRecord::Bogacs::FalsePool
39
+ pool_class = ActiveRecord::Bogacs::DefaultPool
40
40
  ActiveRecord::ConnectionAdapters::ConnectionHandler.connection_pool_class = pool_class
41
41
  end
42
42
  ```
43
43
 
44
- pools are expected to work with older ActiveRecord versions, if not let us know.
44
+ Alternatively, for `FalsePool`, there's a configuration convention (no need to path
45
+ and set the connection_pool_class) :
46
+ ```yaml
47
+ production:
48
+ adapter: mysql2
49
+ <% if $servlet_context %>
50
+ jndi: java:comp/env/jdbc/mydb
51
+ pool: false # use AR::Bogacs::FalsePool
52
+ <% else %>
53
+ port: <%= ENV['DATABASE_PORT'] || 3306 %>
54
+ database: mydb
55
+ # ...
56
+ <% end %>
57
+ ```
58
+
59
+ This works only as long as one doesn't set a custom connection handler, since
60
+ *Bogacs* only sets up its custom `ActiveRecord::Base.default_connection_handler`.
61
+
62
+ Pools are expected to work with older ActiveRecord versions: 3.x as well as 4.x.
45
63
 
46
64
  ### [Default Pool][2]
47
65
 
48
- Meant as a back-port for users stuck with old Rails versions (< 4.0) on production,
49
- facing potential (pool related) concurrency bugs e.g. with high-loads under JRuby.
66
+ Meant, primarily, as a back-port for users stuck with old Rails versions (< 4.0)
67
+ on production, facing potential (pool related) concurrency bugs e.g. with highly
68
+ concurrent loads under JRuby, although it also enhances some of the thread locking
69
+ issues present in 4.x's pool.
50
70
 
51
71
  Based on pool code from 4.x (which works much better than any previous version),
52
72
  with a few minor tunings and extensions such as `pool_initial: 0.5` which allows
@@ -56,7 +76,7 @@ to specify how many connections to initialize in advance when the pool is create
56
76
 
57
77
  The false pool won't do any actual pooling, it is assumed that an underlying pool
58
78
  is configured. Still, it does maintain a hash of AR connections mapped to threads.
59
- Ignores pool related configurations such as `pool: 42` or `checkout_timeout: 2.5`.
79
+ Ignores pool related configuration such as `pool: 42` or `checkout_timeout: 2.5`.
60
80
 
61
81
  **NOTE:** be sure to configure an underlying pool e.g. with Trinidad (using the
62
82
  default Tomcat JDBC pool) :
@@ -70,7 +90,7 @@ default Tomcat JDBC pool) :
70
90
  mysql_dbpool:
71
91
  url: jdbc:mysql:///my_production
72
92
  username: root
73
- jndi: jdbc/BogacsDB
93
+ jndi: jdbc/MyDB
74
94
  initialSize: <%= ENV['POOL_INITIAL'] || 25 %> # connections created on start
75
95
  maxActive: <%= ENV['POOL_SIZE'] || 100 %> # default 100 (AR pool: size)
76
96
  maxIdle: <%= ENV['POOL_SIZE'] || 100 %> # max connections kept in the pool
@@ -88,7 +108,7 @@ following configuration :
88
108
  ```
89
109
  production:
90
110
  adapter: mysql2
91
- jndi: java:/comp/env/jdbc/BogacsDB
111
+ jndi: java:/comp/env/jdbc/MyDB
92
112
  ```
93
113
 
94
114
  **NOTE:** when using `FalsePool` there's nothing to configure (in *database.yml*)!
@@ -97,7 +117,7 @@ production:
97
117
 
98
118
  This pool allows for a database connection to be "shared" among threads, this is
99
119
  very **dangerous** normally. You will need to understand the underlying driver's
100
- connection whether it is thread-safe.
120
+ connection implementation (whether its thread-safe).
101
121
 
102
122
  You'll need to manually declare blocks that run with a shared connection (**make
103
123
  sure** only read operations happen within such blocks) similar to the built-in
@@ -109,15 +129,15 @@ cache_fetch( [ 'user', user_id ] ) do
109
129
  end
110
130
  ```
111
131
 
112
- The pool will only share connections among such blocks and only if it runs out
113
- of all connections (until than it will always prefer checking out a connection
114
- just like a "regular" pool).
132
+ The pool "might" share connections among such blocks but only if it runs out of
133
+ all connections (pool size is reached), until than it will always prefer checking
134
+ out a connection just like `with_connection` does.
115
135
 
116
- Only tested with ActiveRecord-JDBC-Adapter using the official Postgres' driver.
136
+ Tested with ActiveRecord-JDBC-Adapter using the official Postgres' driver (< 42).
117
137
 
118
138
  ## Copyright
119
139
 
120
- Copyright (c) 2017 [Karol Bucek](http://kares.org).
140
+ Copyright (c) 2018 [Karol Bucek](http://kares.org).
121
141
  See LICENSE (http://en.wikipedia.org/wiki/MIT_License) for details.
122
142
 
123
143
  [0]: http://res.cloudinary.com/kares/image/upload/c_scale,h_600,w_800/v1406451696/bogacs.jpg
@@ -1,6 +1,10 @@
1
- require 'active_record'
2
1
 
3
2
  require 'active_record/bogacs/version'
4
3
  require 'active_record/bogacs/autoload'
5
4
 
6
- require 'active_record/connection_adapters/pool_class.rb'
5
+ if defined?(Rails::Railtie)
6
+ require 'active_record/bogacs/railtie'
7
+ else
8
+ require 'active_record'
9
+ require 'active_record/connection_adapters/pool_class'
10
+ end
@@ -0,0 +1,36 @@
1
+ require 'active_record/version'
2
+
3
+ require 'active_record/connection_adapters/abstract/connection_pool'
4
+
5
+ module ActiveRecord
6
+ module Bogacs
7
+ class ConnectionHandler < ConnectionAdapters::ConnectionHandler
8
+
9
+ if ActiveRecord::VERSION::MAJOR < 5
10
+
11
+ def establish_connection(owner, spec)
12
+ @class_to_pool.clear
13
+ if spec.config[:pool].eql? false
14
+ owner_to_pool[owner.name] = ActiveRecord::Bogacs::FalsePool.new(spec)
15
+ else # super
16
+ owner_to_pool[owner.name] = ActiveRecord::ConnectionAdapters::ConnectionPool.new(spec)
17
+ end
18
+ end
19
+
20
+ else
21
+
22
+ def establish_connection(config) # (spec) in 5.0
23
+ pool = super
24
+ spec = pool.spec
25
+ if spec.config[:pool].eql? false
26
+ owner_to_pool[owner.name] = ActiveRecord::Bogacs::FalsePool.new(spec)
27
+ else
28
+ pool
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,17 @@
1
+ module ActiveRecord
2
+ module Bogacs
3
+ class Railtie < Rails::Railtie
4
+
5
+ initializer 'active_record.bogacs', :before => 'active_record.initialize_database' do |_|
6
+ ActiveSupport.on_load :active_record do
7
+ require 'active_record/bogacs'
8
+
9
+ # support for auto-configuring FalsePool (when config[:pool] set to false) :
10
+ require 'active_record/bogacs/connection_handler'
11
+ ActiveRecord::Base.default_connection_handler = ConnectionHandler.new
12
+ end
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -9,8 +9,8 @@ module ActiveRecord
9
9
  begin
10
10
  require 'thread_safe'
11
11
  rescue
12
- warn "activerecord-bogacs needs gem 'concurrent-ruby', '~> 1.0' (or the old 'thread_safe' gem) " <<
13
- "please install or add it to your Gemfile"
12
+ warn "activerecord-bogacs needs gem 'concurrent-ruby', '~> 1.0' (or the old 'thread_safe' gem)" +
13
+ " please install or add it to your Gemfile"
14
14
  raise e
15
15
  end
16
16
  end
@@ -58,8 +58,8 @@ module ActiveRecord
58
58
  require 'thread_safe'
59
59
  rescue
60
60
  return nil unless required
61
- warn "activerecord-bogacs needs gem 'concurrent-ruby', '~> 1.0' (or the old 'thread_safe' gem) " <<
62
- "please install or add it to your Gemfile"
61
+ warn "activerecord-bogacs needs gem 'concurrent-ruby', '~> 1.0' (or the old 'thread_safe' gem)" +
62
+ " please install or add it to your Gemfile"
63
63
  raise e
64
64
  end
65
65
  end
@@ -71,6 +71,29 @@ module ActiveRecord
71
71
  end
72
72
  end
73
73
 
74
+
75
+ def self.load_monotonic_clock(required = true)
76
+ return const_get :MONOTONIC_CLOCK if const_defined? :MONOTONIC_CLOCK
77
+
78
+ begin
79
+ require 'concurrent/utility/monotonic_time.rb'
80
+ rescue LoadError => e
81
+ return nil unless required
82
+ warn "activerecord-bogacs needs gem 'concurrent-ruby', '~> 1.0'" +
83
+ " please install or add it to your Gemfile"
84
+ raise e
85
+ end
86
+
87
+ if defined? ::Concurrent.monotonic_time
88
+ const_set :MONOTONIC_CLOCK, ::Concurrent.const_get(:GLOBAL_MONOTONIC_CLOCK)
89
+ end
90
+ end
91
+
92
+ # @note Only works when {@link #load_monotonic_time} succeeds.
93
+ #def self.monotonic_time
94
+ # MONOTONIC_CLOCK.get_time # java.lang.System.nanoTime() / 1_000_000_000.0
95
+ #end
96
+
74
97
  end
75
98
  end
76
99
  end
@@ -116,7 +116,7 @@ module ActiveRecord
116
116
  logger && logger.info("[validator] connection ##{conn.object_id} failed to validate: #{e.inspect}")
117
117
  end
118
118
 
119
- # TODO support last_use - only validate if certain amount since use passed
119
+ # TODO support seconds_idle - only validate if certain amount since use passed
120
120
 
121
121
  logger && logger.debug("[validator] found non-active connection ##{conn.object_id} - removing from pool")
122
122
  pool.remove_without_owner conn # not active - remove
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module Bogacs
3
- VERSION = '0.5.1'
3
+ VERSION = '0.6.0'
4
4
  end
5
5
  end
@@ -8,38 +8,42 @@ module ActiveRecord
8
8
 
9
9
  if method_defined? :owner # >= 4.2
10
10
 
11
- attr_reader :last_use
11
+ if ActiveRecord::VERSION::STRING > '5.2'
12
12
 
13
- if ActiveRecord::VERSION::MAJOR > 4
13
+ # THIS IS OUR COMPATIBILITY BASE-LINE
14
14
 
15
- # @private added @last_use
16
- def lease
15
+ elsif ActiveRecord::VERSION::MAJOR > 4
16
+
17
+ # @private added @idle_since
18
+ # this method must only be called while holding connection pool's mutex
19
+ def expire
17
20
  if in_use?
18
- msg = 'Cannot lease connection, '
19
- if @owner == Thread.current
20
- msg += 'it is already leased by the current thread.'
21
- else
22
- msg += "it is already in use by a different thread: #{@owner}. Current thread: #{Thread.current}."
21
+ if @owner != Thread.current
22
+ raise ActiveRecordError, "Cannot expire connection, " \
23
+ "it is owned by a different thread: #{@owner}. " \
24
+ "Current thread: #{Thread.current}."
23
25
  end
24
- raise ActiveRecordError, msg
25
- end
26
26
 
27
- @owner = Thread.current; @last_use = Time.now
27
+ @owner = nil; @idle_since = monotonic_time
28
+ else
29
+ raise ActiveRecordError, "Cannot expire connection, it is not currently leased."
30
+ end
28
31
  end
29
32
 
30
33
  else
31
34
 
32
- # @private removed synchronization + added @last_use
35
+ # @private removed synchronization
33
36
  def lease
34
- if in_use?
35
- if @owner == Thread.current
36
- # NOTE: could do a warning if 4.2.x cases do not end up here ...
37
- end
38
- else
39
- @owner = Thread.current; @last_use = Time.now
37
+ unless in_use?
38
+ @owner = Thread.current
40
39
  end
41
40
  end
42
41
 
42
+ # @private added @idle_since
43
+ def expire
44
+ @owner = nil; @idle_since = monotonic_time
45
+ end
46
+
43
47
  end
44
48
 
45
49
  else
@@ -69,7 +73,7 @@ module ActiveRecord
69
73
  end
70
74
 
71
75
  def expire
72
- @in_use = false; @owner = nil
76
+ @in_use = false; @owner = nil; @idle_since = monotonic_time
73
77
  end
74
78
 
75
79
  else
@@ -83,13 +87,39 @@ module ActiveRecord
83
87
  end
84
88
 
85
89
  def expire
86
- @owner = nil
90
+ @owner = nil; @idle_since = monotonic_time
87
91
  end
88
92
 
89
93
  end
90
94
 
91
95
  end
92
96
 
97
+ unless method_defined? :seconds_idle # >= 5.2
98
+
99
+ if ActiveRecord::Bogacs::ThreadSafe.load_monotonic_clock(false)
100
+ include ActiveRecord::Bogacs::ThreadSafe
101
+
102
+ def monotonic_time; MONOTONIC_CLOCK.get_time end
103
+ private :monotonic_time
104
+
105
+ else
106
+
107
+ def monotonic_time; nil end
108
+ private :monotonic_time
109
+
110
+ warn "activerecord-bogacs failed to load 'concurrent-ruby', '~> 1.0', seconds_idle won't work" if $VERBOSE
111
+
112
+ end
113
+
114
+ # Seconds since this connection was returned to the pool
115
+ def seconds_idle # :nodoc:
116
+ return 0 if in_use?
117
+ time = monotonic_time
118
+ time - ( @idle_since || time )
119
+ end
120
+
121
+ end
122
+
93
123
  end
94
124
  end
95
- end
125
+ end
@@ -0,0 +1 @@
1
+ require 'active_record/bogacs' # auto-loading with gem '...' declarations
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-bogacs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Karol Bucek
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-26 00:00:00.000000000 Z
11
+ date: 2018-05-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -68,9 +68,11 @@ files:
68
68
  - activerecord-bogacs.gemspec
69
69
  - lib/active_record/bogacs.rb
70
70
  - lib/active_record/bogacs/autoload.rb
71
+ - lib/active_record/bogacs/connection_handler.rb
71
72
  - lib/active_record/bogacs/default_pool.rb
72
73
  - lib/active_record/bogacs/false_pool.rb
73
74
  - lib/active_record/bogacs/pool_support.rb
75
+ - lib/active_record/bogacs/railtie.rb
74
76
  - lib/active_record/bogacs/reaper.rb
75
77
  - lib/active_record/bogacs/shareable_pool.rb
76
78
  - lib/active_record/bogacs/thread_safe.rb
@@ -80,6 +82,7 @@ files:
80
82
  - lib/active_record/connection_adapters/adapter_compat.rb
81
83
  - lib/active_record/connection_adapters/pool_class.rb
82
84
  - lib/active_record/shared_connection.rb
85
+ - lib/activerecord-bogacs.rb
83
86
  - test/active_record/bogacs/default_pool_test.rb
84
87
  - test/active_record/bogacs/false_pool_test.rb
85
88
  - test/active_record/bogacs/reaper_test.rb