次は ConnectionPool について.ひとまず関連部分を出すために % rak ‘ConnectionPool’ –ruby で
activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
19| # connection back in. ConnectionPool is completely thread-safe, and will
21| # as long as ConnectionPool's contract is correctly followed. It will also
24| # connection anyway, then ConnectionPool will wait until some other thread
57| class ConnectionPool
60| # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
63| # this ConnectionPool.
65| # The default ConnectionPool maximum size is 5.
255| # ConnectionHandler is a collection of ConnectionPool objects. It is used
288| @connection_pools[name] = ConnectionAdapters::ConnectionPool.new(spec)
と言うことなので activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb の動作を調べる.まずは ActiveRecord::ConnectionAdapters::ConnectionPool#initialize から
65 | # The default ConnectionPool maximum size is 5.
66 | def initialize(spec)
67 | @spec = spec
68 | # The cache of reserved connections mapped to threads
69 | @reserved_connections = {}
70 | # The mutex used to synchronize pool access
71 | @connection_mutex = Monitor.new
72 | @queue = @connection_mutex.new_cond
73 | # default 5 second timeout
74 | @timeout = spec.config[:wait_timeout] || 5
75 | # default max pool size to 5
76 | @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
77 | @connections = []
78 | @checked_out = []
79 | end
80 |
81 | # Retrieve the connection associated with the current thread, or call
82 | # #checkout to obtain one if necessary.
83 | #
84 | # #connection can be called any number of times; the connection is
85 | # held in a hash keyed by the thread id.
86 | def connection
87 | if conn = @reserved_connections[current_connection_id]
88 | conn
89 | else
90 | @reserved_connections[current_connection_id] = checkout
91 | end
92 | end
93 |
ActiveRecord::ConnectionAdapters::ConnectionPool#connection で現在のコネクションが取得できて,
218 | def current_connection_id #:nodoc:
219 | Thread.current.object_id
220 | end
どのコネクションを使うかには,Thread.current オブジェクトのID を使ってるらしい.
287 | def establish_connection(name, spec)
288 | @connection_pools[name] = ConnectionAdapters::ConnectionPool.new(spec)
289 | end
ここで初心に戻ると,ActiveRecord は通常 establish_connection で接続しに行くので,該当部分を activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb から見ると
49 | def self.establish_connection(spec = nil)
50 | case spec
51 | when nil
52 | raise AdapterNotSpecified unless defined? RAILS_ENV
53 | establish_connection(RAILS_ENV)
54 | when ConnectionSpecification
55 | @@connection_handler.establish_connection(name, spec)
56 | when Symbol, String
57 | if configuration = configurations[spec.to_s]
58 | establish_connection(configuration)
59 | else
60 | raise AdapterNotSpecified, "#{spec} database is not configured"
61 | end
62 | else
establish_connection の引数に ConnectionSpecification クラスが指定されていれば,@@connection_handler の establish_connection を呼ぶ.@@connection_handler の establish_connection というのは
10 | # The connection handler
11 | cattr_accessor :connection_handler, :instance_writer => false
12 | @@connection_handler = ConnectionAdapters::ConnectionHandler.new
で,ActiveRecord::ConnectionAdapters::ConnectionHandler#establish_connection が
287 | def establish_connection(name, spec)
288 | @connection_pools[name] = ConnectionAdapters::ConnectionPool.new(spec)
289 | end
なので,上に書いてある ActiveRecord::ConnectionAdapters::ConnectionPool#connection に戻る.そこで現状のスレッド(Thread.current)で接続が無い(else)場合は checkout が呼ばれ
173 | def checkout
174 | # Checkout an available connection
175 | @connection_mutex.synchronize do
176 | loop do
177 | conn = if @checked_out.size < @connections.size
178 | checkout_existing_connection
179 | elsif @connections.size < @size
180 | checkout_new_connection
181 | end
182 | return conn if conn
183 | # No connections available; wait for one
184 | if @queue.wait(@timeout)
185 | next
186 | else
187 | # try looting dead threads
188 | clear_stale_cached_connections!
189 | if @size == @checked_out.size
190 | raise ConnectionTimeoutError, "could not obtain a database connection within #{@timeout} seconds. The pool size is currently #{@size}, perhaps you need to increase it?"
191 | end
192 | end
193 | end
194 | end
195 | end
なので @connection_mutex.synchronize = Mutex#synchronize によって他のスレッドと同期がとられて,データベースコネクションの Pooling が行われる.
続く.
comments powered by Disqus