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