activerecord-bogacs 0.1.0
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 +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +26 -0
- data/Gemfile +33 -0
- data/LICENSE.txt +22 -0
- data/README.md +124 -0
- data/Rakefile +167 -0
- data/activerecord-bogacs.gemspec +26 -0
- data/lib/active_record/bogacs.rb +55 -0
- data/lib/active_record/bogacs/default_pool.rb +672 -0
- data/lib/active_record/bogacs/false_pool.rb +259 -0
- data/lib/active_record/bogacs/pool_support.rb +21 -0
- data/lib/active_record/bogacs/shareable_pool.rb +255 -0
- data/lib/active_record/bogacs/version.rb +5 -0
- data/lib/active_record/connection_adapters/adapter_compat.rb +57 -0
- data/lib/active_record/shared_connection.rb +24 -0
- data/test/active_record/bogacs/default_pool_test.rb +34 -0
- data/test/active_record/bogacs/false_pool_test.rb +200 -0
- data/test/active_record/bogacs/shareable_pool/connection_pool_test.rb +186 -0
- data/test/active_record/bogacs/shareable_pool/connection_sharing_test.rb +429 -0
- data/test/active_record/bogacs/shareable_pool_helper.rb +81 -0
- data/test/active_record/builtin_pool_test.rb +18 -0
- data/test/active_record/connection_pool_test_methods.rb +336 -0
- data/test/test_helper.rb +304 -0
- metadata +130 -0
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'active_record/connection_adapters/abstract_adapter'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
AbstractAdapter.class_eval do
|
6
|
+
|
7
|
+
attr_accessor :pool unless method_defined? :pool
|
8
|
+
|
9
|
+
unless method_defined? :owner
|
10
|
+
|
11
|
+
attr_reader :owner
|
12
|
+
|
13
|
+
if method_defined? :in_use?
|
14
|
+
|
15
|
+
def lease
|
16
|
+
unless in_use?
|
17
|
+
@owner = Thread.current; @in_use = true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def expire
|
22
|
+
@in_use = false; @owner = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
else
|
26
|
+
|
27
|
+
alias :in_use? :owner
|
28
|
+
|
29
|
+
def lease
|
30
|
+
unless in_use?
|
31
|
+
@owner = Thread.current
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def expire
|
36
|
+
@owner = nil
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
alias :in_use? :owner
|
42
|
+
|
43
|
+
def lease
|
44
|
+
unless in_use?
|
45
|
+
@owner = Thread.current
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def expire
|
50
|
+
@owner = nil
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module SharedConnection
|
3
|
+
|
4
|
+
def with_shared_connection(&block)
|
5
|
+
ActiveRecord::SharedConnection.with_shared_connection(self.class, &block)
|
6
|
+
end
|
7
|
+
|
8
|
+
# NOTE: shareable pool loaded and setup to be used (from an initializer) :
|
9
|
+
if ActiveRecord::Base.connection_pool.respond_to?(:with_shared_connection)
|
10
|
+
|
11
|
+
def self.with_shared_connection(model = ActiveRecord::Base, &block)
|
12
|
+
model.connection_pool.with_shared_connection(&block)
|
13
|
+
end
|
14
|
+
|
15
|
+
else
|
16
|
+
|
17
|
+
def self.with_shared_connection(model = ActiveRecord::Base, &block)
|
18
|
+
model.connection_pool.with_connection(&block) # default pool is used
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.expand_path('../../test_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
ActiveRecord::Bogacs::DefaultPool.class_eval do
|
4
|
+
# ...
|
5
|
+
end
|
6
|
+
|
7
|
+
module ActiveRecord
|
8
|
+
module Bogacs
|
9
|
+
class DefaultPoolTest < Test::Unit::TestCase
|
10
|
+
|
11
|
+
include ConnectionAdapters::ConnectionPoolTestMethods
|
12
|
+
|
13
|
+
def config; AR_CONFIG end
|
14
|
+
|
15
|
+
def setup
|
16
|
+
super
|
17
|
+
@pool = DefaultPool.new ActiveRecord::Base.connection_pool.spec
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_prefills_initial_connections
|
21
|
+
@pool.disconnect!
|
22
|
+
spec = ActiveRecord::Base.connection_pool.spec.dup
|
23
|
+
spec.instance_variable_set :@config, spec.config.merge(:pool_initial => 1.0)
|
24
|
+
@pool = DefaultPool.new spec
|
25
|
+
assert_equal @pool.size, @pool.connections.size
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_does_not_prefill_connections_by_default
|
29
|
+
assert_equal 0, @pool.connections.size
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
require File.expand_path('../../test_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require 'atomic'
|
4
|
+
|
5
|
+
ActiveRecord::Bogacs::FalsePool.class_eval do
|
6
|
+
# ...
|
7
|
+
end
|
8
|
+
|
9
|
+
module ActiveRecord
|
10
|
+
module Bogacs
|
11
|
+
class FalsePool
|
12
|
+
|
13
|
+
class TestBase < ::Test::Unit::TestCase
|
14
|
+
# extend Bogacs::TestHelper
|
15
|
+
extend Bogacs::JndiTestHelper
|
16
|
+
|
17
|
+
def self.startup
|
18
|
+
return if self == TestBase
|
19
|
+
|
20
|
+
ActiveRecord::Base.establish_connection AR_CONFIG
|
21
|
+
|
22
|
+
ActiveRecord::Base.connection.jdbc_connection # force connection
|
23
|
+
current_config = Bogacs::TestHelper.current_connection_config
|
24
|
+
|
25
|
+
ActiveRecord::Base.connection_pool.disconnect!
|
26
|
+
|
27
|
+
setup_jdbc_context
|
28
|
+
bind_data_source init_data_source current_config
|
29
|
+
|
30
|
+
ConnectionAdapters::ConnectionHandler.connection_pool_class = FalsePool
|
31
|
+
ActiveRecord::Base.establish_connection jndi_config
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.shutdown
|
35
|
+
return if self == TestBase
|
36
|
+
|
37
|
+
ActiveRecord::Base.connection_pool.disconnect!
|
38
|
+
ConnectionAdapters::ConnectionHandler.connection_pool_class = ConnectionAdapters::ConnectionPool
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
class ConnectionPoolWrappingTomcatJdbcTest < TestBase
|
44
|
+
|
45
|
+
@@data_source = nil
|
46
|
+
def self.init_data_source(config)
|
47
|
+
@@data_source = init_tomcat_jdbc_data_source(config)
|
48
|
+
end
|
49
|
+
|
50
|
+
include ConnectionAdapters::ConnectionPoolTestMethods
|
51
|
+
|
52
|
+
def setup
|
53
|
+
@pool = FalsePool.new ActiveRecord::Base.connection_pool.spec
|
54
|
+
end
|
55
|
+
|
56
|
+
def teardown
|
57
|
+
@@data_source.send(:close, true) if @@data_source
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_uses_false_pool_and_can_execute_query
|
61
|
+
assert_instance_of ActiveRecord::Bogacs::FalsePool, ActiveRecord::Base.connection_pool
|
62
|
+
assert ActiveRecord::Base.connection.exec_query('SELECT 42')
|
63
|
+
end
|
64
|
+
|
65
|
+
# adjust ConnectionAdapters::ConnectionPoolTestMethods :
|
66
|
+
|
67
|
+
undef :test_checkout_fairness
|
68
|
+
undef :test_checkout_fairness_by_group
|
69
|
+
|
70
|
+
undef :test_released_connection_moves_between_threads
|
71
|
+
|
72
|
+
undef :test_reap_inactive
|
73
|
+
|
74
|
+
undef :test_automatic_reconnect= # or does automatic_reconnect make sense?
|
75
|
+
|
76
|
+
undef :test_removing_releases_latch
|
77
|
+
|
78
|
+
# @override
|
79
|
+
def test_remove_connection
|
80
|
+
conn = pool.checkout
|
81
|
+
assert conn.in_use?
|
82
|
+
|
83
|
+
#length = pool.connections.size
|
84
|
+
pool.remove conn
|
85
|
+
assert conn.in_use?
|
86
|
+
#assert_equal(length - 1, pool.connections.length)
|
87
|
+
ensure
|
88
|
+
conn.close if conn
|
89
|
+
end
|
90
|
+
|
91
|
+
# @override
|
92
|
+
def test_full_pool_exception
|
93
|
+
# ~ pool_size.times { pool.checkout }
|
94
|
+
threads_ready = Queue.new; threads_block = Atomic.new(0); threads = []
|
95
|
+
pool_size.times do |i|
|
96
|
+
threads << Thread.new do
|
97
|
+
begin
|
98
|
+
conn = ActiveRecord::Base.connection
|
99
|
+
threads_block.update { |v| v + 1 }
|
100
|
+
threads_ready << i
|
101
|
+
while threads_block.value != -1 # await
|
102
|
+
sleep(0.005)
|
103
|
+
end
|
104
|
+
rescue => e
|
105
|
+
puts "block thread failed: #{e.inspect}"
|
106
|
+
ensure
|
107
|
+
conn && conn.close
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
pool_size.times { threads_ready.pop } # awaits
|
112
|
+
|
113
|
+
assert_raise(ConnectionTimeoutError) do
|
114
|
+
ActiveRecord::Base.connection # ~ pool.checkout
|
115
|
+
end
|
116
|
+
|
117
|
+
ensure
|
118
|
+
#connection && connection.close
|
119
|
+
threads_block && threads_block.swap(-1)
|
120
|
+
threads && threads.each(&:join)
|
121
|
+
end
|
122
|
+
|
123
|
+
# @override
|
124
|
+
def test_full_pool_blocks
|
125
|
+
t1_ready = Queue.new; t1_block = Queue.new
|
126
|
+
t1 = Thread.new do
|
127
|
+
begin
|
128
|
+
conn = ActiveRecord::Base.connection
|
129
|
+
t1_ready.push(conn)
|
130
|
+
t1_block.pop # await
|
131
|
+
rescue => e
|
132
|
+
puts "t1 thread failed: #{e.inspect}"
|
133
|
+
ensure
|
134
|
+
conn && conn.close
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
threads_ready = Queue.new; threads_block = Atomic.new(0); threads = []
|
139
|
+
(pool_size - 1).times do |i|
|
140
|
+
threads << Thread.new do
|
141
|
+
begin
|
142
|
+
conn = ActiveRecord::Base.connection
|
143
|
+
threads_block.update { |v| v + 1 }
|
144
|
+
threads_ready << i
|
145
|
+
while threads_block.value != -1 # await
|
146
|
+
sleep(0.005)
|
147
|
+
end
|
148
|
+
rescue => e
|
149
|
+
puts "block thread failed: #{e.inspect}"
|
150
|
+
ensure
|
151
|
+
conn && conn.close
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
(pool_size - 1).times { threads_ready.pop } # awaits
|
156
|
+
|
157
|
+
connection = t1_ready.pop
|
158
|
+
t1_jdbc_connection = connection.jdbc_connection(true)
|
159
|
+
|
160
|
+
# pool = ActiveRecord::Base.connection_pool
|
161
|
+
|
162
|
+
t2 = Thread.new do
|
163
|
+
begin
|
164
|
+
ActiveRecord::Base.connection
|
165
|
+
rescue => e
|
166
|
+
puts "t2 thread failed: #{e.inspect}"
|
167
|
+
end
|
168
|
+
end; sleep(0.1)
|
169
|
+
|
170
|
+
# make sure our thread is in the timeout section
|
171
|
+
# Thread.pass until t2.status == "sleep"
|
172
|
+
|
173
|
+
sleep(0.02); assert t2.alive?
|
174
|
+
sleep(0.03); assert t2.alive?
|
175
|
+
|
176
|
+
t1_block.push(:release); t1.join
|
177
|
+
|
178
|
+
sleep(0.01); assert_not_equal 'sleep', t2.status
|
179
|
+
|
180
|
+
if defined? JRUBY_VERSION
|
181
|
+
if connection2 = t2.join.value
|
182
|
+
assert_equal t1_jdbc_connection, connection2.jdbc_connection(true)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
ensure
|
187
|
+
#connection && connection.close
|
188
|
+
threads_block && threads_block.swap(-1)
|
189
|
+
threads && threads.each(&:join)
|
190
|
+
end
|
191
|
+
|
192
|
+
private
|
193
|
+
|
194
|
+
def pool_size; @@data_source.max_active end
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
require File.expand_path('../shareable_pool_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Bogacs
|
5
|
+
class ShareablePool
|
6
|
+
|
7
|
+
class ConnectionPoolTest < TestBase
|
8
|
+
|
9
|
+
include ConnectionAdapters::ConnectionPoolTestMethods
|
10
|
+
|
11
|
+
def setup
|
12
|
+
@pool = ShareablePool.new ActiveRecord::Base.connection_pool.spec
|
13
|
+
end
|
14
|
+
|
15
|
+
if ActiveRecord::VERSION::MAJOR < 4
|
16
|
+
# TODO override with similar (back-ported) tests :
|
17
|
+
undef :test_remove_connection
|
18
|
+
undef :test_remove_connection_for_thread
|
19
|
+
undef :test_removing_releases_latch
|
20
|
+
|
21
|
+
undef :test_reap_and_active
|
22
|
+
undef :test_reap_inactive
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
class PoolAPITest < TestBase
|
28
|
+
|
29
|
+
def setup; ActiveRecord::Base.connection end
|
30
|
+
# def teardown; ActiveRecord::Base.connection_pool.reap end
|
31
|
+
|
32
|
+
def test_is_setup
|
33
|
+
assert ActiveRecord::Base.connection_pool.is_a? ShareablePool
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_can_checkout_a_connection
|
37
|
+
assert ActiveRecord::Base.connection.exec_query('SELECT 42')
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_connected?
|
41
|
+
assert ActiveRecord::Base.connected?
|
42
|
+
begin
|
43
|
+
ActiveRecord::Base.connection_pool.disconnect!
|
44
|
+
assert ! ActiveRecord::Base.connected?
|
45
|
+
ensure
|
46
|
+
ActiveRecord::Base.connection
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_active?
|
51
|
+
conn = ActiveRecord::Base.connection
|
52
|
+
conn.exec_query('SELECT 42')
|
53
|
+
assert conn.active?
|
54
|
+
assert reserved_connections.size > 0
|
55
|
+
begin
|
56
|
+
ActiveRecord::Base.clear_active_connections!
|
57
|
+
assert_equal 0, reserved_connections.size
|
58
|
+
ensure
|
59
|
+
ActiveRecord::Base.connection
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_disconnect!
|
64
|
+
ActiveRecord::Base.connection
|
65
|
+
threads = []
|
66
|
+
threads << Thread.new { ActiveRecord::Base.connection }
|
67
|
+
threads << Thread.new { ActiveRecord::Base.connection }
|
68
|
+
threads.each(&:join)
|
69
|
+
|
70
|
+
begin
|
71
|
+
ActiveRecord::Base.connection_pool.disconnect!
|
72
|
+
assert_equal 0, reserved_connections.size
|
73
|
+
assert_equal 0, connections.size
|
74
|
+
assert_equal 0, shared_connections.size
|
75
|
+
ensure
|
76
|
+
ActiveRecord::Base.connection
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_clear_reloadable_connections!
|
81
|
+
ActiveRecord::Base.connection
|
82
|
+
threads = []
|
83
|
+
threads << Thread.new { ActiveRecord::Base.connection }
|
84
|
+
threads << Thread.new { ActiveRecord::Base.connection }
|
85
|
+
threads.each(&:join)
|
86
|
+
|
87
|
+
begin
|
88
|
+
ActiveRecord::Base.connection_pool.clear_reloadable_connections!
|
89
|
+
assert_equal 0, reserved_connections.size
|
90
|
+
assert connections.size > 0 # returned
|
91
|
+
assert_equal 0, shared_connections.size
|
92
|
+
ensure
|
93
|
+
ActiveRecord::Base.connection
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_remove
|
98
|
+
conn = ActiveRecord::Base.connection
|
99
|
+
assert connections.include? conn
|
100
|
+
begin
|
101
|
+
connection_pool.remove(conn)
|
102
|
+
refute connections.include?(conn)
|
103
|
+
refute shared_connection?(conn)
|
104
|
+
ensure
|
105
|
+
ActiveRecord::Base.connection
|
106
|
+
end
|
107
|
+
end if ActiveRecord::VERSION::MAJOR >= 4
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
class PoolAPIWithSharedConnectionTest < PoolAPITest
|
112
|
+
|
113
|
+
def setup
|
114
|
+
with_shared_connection do
|
115
|
+
@shared_connection = ActiveRecord::Base.connection
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def teardown
|
120
|
+
connection_pool.release_shared_connection(@shared_connection)
|
121
|
+
super
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
class CustomAPITest < TestBase
|
127
|
+
|
128
|
+
def setup
|
129
|
+
connection_pool.disconnect!
|
130
|
+
end
|
131
|
+
|
132
|
+
def teardown
|
133
|
+
clear_active_connections!; clear_shared_connections!
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_with_shared_connection
|
137
|
+
shared_connection = nil
|
138
|
+
assert shared_connections.empty?
|
139
|
+
begin
|
140
|
+
with_shared_connection do |connection|
|
141
|
+
assert shared_connection = connection
|
142
|
+
assert shared_connections.get(connection)
|
143
|
+
assert connections.include?(connection)
|
144
|
+
end
|
145
|
+
ensure
|
146
|
+
connection_pool.remove(shared_connection) if shared_connection
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_with_shared_connection_disconnected
|
151
|
+
shared_connection = nil
|
152
|
+
begin
|
153
|
+
connections_size = connections.size
|
154
|
+
with_shared_connection do |connection|
|
155
|
+
assert shared_connection = connection
|
156
|
+
assert shared_connection?(connection)
|
157
|
+
assert_equal connections_size + 1, connections.size
|
158
|
+
end
|
159
|
+
ensure
|
160
|
+
connection_pool.remove(shared_connection) if shared_connection
|
161
|
+
ActiveRecord::Base.connection
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def test_release_shared_connection
|
166
|
+
begin
|
167
|
+
with_shared_connection do |connection|
|
168
|
+
assert shared_connection?(connection)
|
169
|
+
refute available_connection?(connection)
|
170
|
+
|
171
|
+
connection_pool.release_shared_connection(connection)
|
172
|
+
|
173
|
+
refute shared_connection?(connection)
|
174
|
+
assert available_connection?(connection)
|
175
|
+
end
|
176
|
+
ensure
|
177
|
+
connection_pool.disconnect!
|
178
|
+
ActiveRecord::Base.connection
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|