activerecord-bogacs 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ module ActiveRecord
2
+ module Bogacs
3
+ VERSION = '0.1.0'
4
+ end
5
+ end
@@ -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