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 +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +37 -17
- data/lib/active_record/bogacs.rb +6 -2
- data/lib/active_record/bogacs/connection_handler.rb +36 -0
- data/lib/active_record/bogacs/railtie.rb +17 -0
- data/lib/active_record/bogacs/thread_safe.rb +27 -4
- data/lib/active_record/bogacs/validator.rb +1 -1
- data/lib/active_record/bogacs/version.rb +1 -1
- data/lib/active_record/connection_adapters/adapter_compat.rb +52 -22
- data/lib/activerecord-bogacs.rb +1 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a11630875cf14ee4c1ea37721a04a1966eeb488874d23c833075e42c2e435852
|
4
|
+
data.tar.gz: 77016cc2628013c4a1791403fcbf65ad488f8db4f6a091a9659e1e0b523c7e38
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89a482ca85813ef8b769355a0a42ffe0317024022c0da7ad337084029ad455976865199806b6f79fbd0b79c1288ec7f6a1654ec9aea66ec07b544b28329f5dcc
|
7
|
+
data.tar.gz: 624c0fe63b5ff31ef51abeb2dd537fe3ef2bddad81f03cd94df38fb7aafea3b93635a844554bb7597ec08fc551215c016e1c1ebcb3817230220455919db1ade3
|
data/LICENSE.txt
CHANGED
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
|
-
**
|
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
|
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 "
|
37
|
+
# sample AR-Bogacs setup using the "default" pool :
|
38
38
|
if Rails.env.production?
|
39
|
-
pool_class = ActiveRecord::Bogacs::
|
39
|
+
pool_class = ActiveRecord::Bogacs::DefaultPool
|
40
40
|
ActiveRecord::ConnectionAdapters::ConnectionHandler.connection_pool_class = pool_class
|
41
41
|
end
|
42
42
|
```
|
43
43
|
|
44
|
-
|
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)
|
49
|
-
facing potential (pool related) concurrency bugs e.g. with
|
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
|
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/
|
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/
|
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
|
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
|
113
|
-
|
114
|
-
just like
|
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
|
-
|
136
|
+
Tested with ActiveRecord-JDBC-Adapter using the official Postgres' driver (< 42).
|
117
137
|
|
118
138
|
## Copyright
|
119
139
|
|
120
|
-
Copyright (c)
|
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
|
data/lib/active_record/bogacs.rb
CHANGED
@@ -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
|
-
|
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
|
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
|
@@ -8,38 +8,42 @@ module ActiveRecord
|
|
8
8
|
|
9
9
|
if method_defined? :owner # >= 4.2
|
10
10
|
|
11
|
-
|
11
|
+
if ActiveRecord::VERSION::STRING > '5.2'
|
12
12
|
|
13
|
-
|
13
|
+
# THIS IS OUR COMPATIBILITY BASE-LINE
|
14
14
|
|
15
|
-
|
16
|
-
|
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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
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
|
35
|
+
# @private removed synchronization
|
33
36
|
def lease
|
34
|
-
|
35
|
-
|
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.
|
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-
|
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
|