内容简介:Rails with_connection, reap 與 connection leak
在之前遇到 疑似 rails connection leak 之後,就一直大概知道 ActiveRecord 的 with_connection 可以避免 connection leak。前陣子在設定 RabbitMQ 的時候,出現了 connection pool timeout 的 error,在 debug 的時候才發現原來我好像一直沒有親眼看過 Rails leak connection 的時候會長得怎樣 XD
所以我試圖用一些方式讓 Rails leak database connection,發現好像沒有很單純阿。 在這篇文章想要把一些嘗試的過程記錄下來和大家分享。
關於 connection leak 和 connection pool 的介紹可以看這邊。
重現問題
在另外開的 thread 裡面,如果有使用到 connection 而忘記 checkin 回去的話,理論上這個 connection 就會 leak 掉。
但是要怎麼重現這樣的狀況呢?
下面是概略的環境設定:
- Rails 4.2.8
- 首先我試著把測試的環境建立成一個 rake job
- 並且把 connection pool 的 size 調成 3
task :test_conn => :environment do
pool = ActiveRecord::Base.connection_pool
threads = 3.times.map do
Thread.new do
pool.checkout
end
end
threads.each(&:join)
pool.checkout
end
在這邊,我開了 3 個 threads,並且在每一個 thread 裡面 checkout 一個 connection 出來。 在確定 connection 都被 checkout 出來了 ( threads.each(&:join) ) 之後, 理論上因為所有pool裡的 connection (3個) 都 leak 掉了,所以當我們要再 checkout 一個 connection 出來的時候,應該要出現 timeout 的 error。
但這個 rake job 跑下去的時候, 完全沒有任何的 error 出現 QQ
後來發現,connection 有一個 method 叫 in_use? 可以來幫我們確認 connection 有沒有正在被使用。 當 connection 沒有在被使用的時候,他會回傳 nil ,而當正在被使用的時候,他會回傳正在使用的那個 thread。
於是把剛才的 rake job 加上一些 debug 的資訊變成這樣:
def log_connections conns
conns.each do |idx, conn|
puts "#{idx}: #{conn.object_id}; #{conn.in_use?}"
end
end
task :test_conn_w_log => :environment do
pool = ActiveRecord::Base.connection_pool
conns = {}
threads = 3.times.map do |i|
Thread.new(i) do |index|
conns[index] = pool.checkout
end
end
threads.each(&:join)
puts "after joining"
log_connections(conns)
pool.checkout
puts "after re-checkout"
log_connections(conns)
end
得到的 output 是這樣:
after joining 0: 47195627544380; #<Thread:0x0055d92d3bd1a0> 1: 47195627073520; #<Thread:0x0055d92d3bc408> 2: 47195626859620; #<Thread:0x0055d92d367d90> after re-checkout 0: 47195627544380; #<Thread:0x0055d92952a258> 1: 47195627073520; 2: 47195626859620;
我們發現在 join 之後,也就是在 thread 已經結束了之後,connection 都還是在 “使用中” 的狀態。 然後在這樣的狀況下 checkout,不僅沒有問題,反而其他的 connection 還變成 “非使用中” 的狀態了。
在實際看了 ActiveRecord connection pool 的 source code 之後, 發現在 每次企圖 checkout 的時候,如果 pool 裡面沒有可用的 connection 了,ActiveRecord 會試圖做一個叫作 reap 的行為,這個動作會把 “現在在 ‘使用中’ 但 owner 已經死掉” 的那些 connection 回收起來。 這也就是為什麼在重新 checkout 之後,另外兩個 connection 又變成 “非使用中” 的狀態了。
但是如果在執行 reap 的時候,所有正在使用的 connection 的 owner 都還沒死,那這時候 reap 就無法回收任何 connection。 即使在 timeout 的限制(5秒)之內,有其他的 thread 已經用完 connection 了,這些用完的 connection 還是無法被使用。
task :test_conn_cannot_reap => :environment do
pool = ActiveRecord::Base.connection_pool
conns = {}
threads = 4.times.map do |i|
Thread.new(i) do |index|
conns[index] = pool.checkout
puts "got connection in thread #{index}"
sleep 1
end
end
threads.each(&:join)
puts "after joining"
log_connections(conns)
pool.checkout
puts "after re-checkout"
log_connections(conns)
end
這樣得到的 output 會是:
got connection in thread 0 got connection in thread 1 got connection in thread 2 rake aborted! ActiveRecord::ConnectionTimeoutError: could not obtain a database connection within 5.000 seconds (waited 5.000 seconds)
但是這個時候, 如果配合上上面的 with_connection 就沒問題了:
task :test_conn_w_with_connection => :environment do
pool = ActiveRecord::Base.connection_pool
conns = {}
threads = 4.times.map do |i|
Thread.new(i) do |index|
pool.with_connection do |conn|
conns[index] = conn
puts "got connection in thread #{index}"
sleep 1
end
end
end
threads.each(&:join)
puts "after joining"
log_connections(conns)
pool.checkout
puts "after re-checkout"
log_connections(conns)
end
output:
got connection in thread 0 got connection in thread 2 got connection in thread 1 got connection in thread 3 after joining 0: 47069386019060; 2: 47069385769900; 1: 47069385570280; 3: 47069386019060; after re-checkout 0: 47069386019060; 2: 47069385769900; #<Thread:0x00559e5ee72258> 1: 47069385570280; 3: 47069386019060;
結論
Connection count 的限制是在各種時候都必須要小心的事情。再加上 thread 這類有”時間”性質的變因加入之後,會讓整個東西變得更複雜。 基本上我個人是比較偏好在寫 Rails 的時候,讓 library/framework 這層去控制 thread 比較令人安心一點。
也就是說, 如果可以讓 thread 的複雜度不要隨著 application feature 的增加而增加會是比較好的狀況 。
還有,雖然說難歸難,但是找答案的過程還真的是好有趣 der ㄎㄎ
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
形式感+:网页视觉设计创意拓展与快速表现
晋小彦 / 清华大学出版社 / 2014-1-1 / 59.00元
网页设计师从早年的综合性工作中分化出来,形成了相对独立的专业岗位,网页设计也不再是单纯的软件应用,它衍生出了许多独立的研究方向,当网站策划、交互体验都逐渐独立之后,形式感的突破和表现成为网页视觉设计的一项重要工作。随着时代的发展,网页设计更接近于一门艺术。网络带宽和硬件的发展为网页提供了使用更大图片、动画甚至视频的权利,而这些也为视觉设计师提供了更多表现的空间。另外多终端用户屏幕(主要是各种移动设......一起来看看 《形式感+:网页视觉设计创意拓展与快速表现》 这本书的介绍吧!