主動攔截 Rails exception 錯誤

喔,炸了。

相信以上畫面大家都不陌生,這是一個標準的 Rails app 錯誤頁面。雖然我們努力避免,但總是有出錯的時候,一個上 production mode 的專業 Rails app 絕不會痴痴地等待使用者告訴你網站炸了,而是要能夠主動通知及紀錄下這個錯誤例外(exception),好讓我可以 trace error、fixed bug 甚至在發生錯誤沒多久就可以 E-mail 告訴這位使用者苦主發生了什麼事情。

解法有幾種:

  1. 安裝 exceiption_notifition,這是個官方的基本 Plugin ,它會在發生例外時寄 email 通知你(們)。

  2. 安裝 ExceptionLogger (作者 defunkt 也是 facebox 的作者 :p ),這個 Plugin 會紀錄到 database,並提供一個後台可以瀏覽。不過這個後台裝起來比較麻煩,例如你還得處理瀏覽權限等等。

  3. 使用 Hoptoad 第三方服務,這是個由知名Rails團隊 thoughtbot 所提供的 Web service。申請帳號之後可以拿到 API key 及 Plugin,於是你的網站發生 exception 的時候就會自動將錯誤訊息送到 Hoptoad 收集起來。Hoptoad 提供了還蠻不錯的後台可以瀏覽。這個解法安裝最簡單,功能又很夠用,還可以統計及追蹤例外處理的情況,我個人十分推薦使用。

github 上應該還有其他的 Plugins 可以幫助你處理這個問題。anyway… 最糟的就是什麼都不做,讓使用者告訴你有東西炸了,然後你再去爬去找 log 檔 :)

和多繽紛樂@Yahoo! Open Hack Day

我們得獎了!和多繽紛樂敝社參加 Y! Open Hack Day 2008 的作品,它是個賓果機產生器,你可以將各式資料扔進去,拉下拉霸之後就會亂數回答一個結果。除了直接提供選項之外,也可以選擇 Yahoo! 生活+、Yahoo! 知識+、Yahoo! 電影、Yahoo! 拍賣、Flickr 相簿、無名熱門相簿、Diggirl 熱門相簿等等資料。

和多獲佳作十萬元

Handlino and David Filo

和多與Y!共同創辦人David Flio的合照

會場音樂很吵(剛好我們的位置被安排在音響前方)、正妹很漂亮、食物飲料不缺、網路雖不快但也算順暢、贈品還不錯豐富,Y! 辦了一場想要大家認真 Coding,但又請了樂團炒熱氣氛的 Party… XD 對我來說,最 high 的不是台上怎麼玩,而是這充分 teamwork 的黑客松過程,在這兩天內和多瘋狂地 commit,一想到想要做什麼,分工一下不用多久就做出來了,我們也在會場內提早公開這個網站讓大家玩(Agile feedback!!)。

雖然超級疲累,不過得獎真的很爽 :D

最佳化 ActiveRecord SQL 查詢

Update(2008/9/5) 補充 named_scope 也可以用在 :select,感謝 tsechingho++

要看 AciveRecord 產生的 SQL 是什麼,除了可以直接 tail -f log/development.log 之外,也可以在 script/console 的情況下透過以下指令直接把 log 直接顯示出來:

ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Base.clear_active_connections!

如此便可以好好在 console 的環境下實驗 ActiveRecord 囉。

我看到關於 ActiveRecord SQL 查詢最佳化方式有這幾種:

1. 使用 :select

只撈需要用到的欄位,特別是如果不需要用到的 text 或 binary 欄位請排除。

Event.find(:all, :select => "id, title, description")

搭配 named_scope 我們可以把常用的 :select 預先設定好,例如:

class User < ActiveRecord::Base named_scope :short, :select => "id, name, email"
end

User.short.find(:all)

2. 使用 :include

使用 :include 避免 N+1 次 queries 的問題。


@events = Event.find(:all, :include => [:group] )


@events.each do |e|
e.group.title
end

如果沒有加 :include 把相關的 groups 一起載入,在迴圈中就會產生 @event.size 次對 group 的個別 SQL 查詢,會非常傷。加了 :include 之後總共只需查詢兩次。

另外一個比較少人知道的是,在設定 Model associations 時,如果有很明顯的情境一定會順道載入二階 association model,可以設定 :include 在 has_many, belongs_to, has_one 上面,例如:


class User < ActiveRecord::Base has_one :foo, :include => [:bar]
end

如此便會在載入 @user.foo 的同時,也會提早載入 @user.foo.bar。

3. 資料庫索引

針對 foreign key 要加上資料庫索引 index。在 migration 上透過 add_index 就可以加上去了。

4. 特定情況下可用 :joins 取代 :include

在只需要用到 :conditions 而不需要載入該 model 的情況下,可以用 :joins 取代 :include。

Group.find(:all, :include => [ :group_memberships ], :conditions => [ "group_memberships.created_at > ?", Time.now - 30.days ] )

因為其中會載入 group_memberships model 只是為了加條件式,而沒有要撈出裡面的資料,所以可以改用 :joins,這其中的差異你看產生出的SQL就知道了 :)


Group.find(:all, :joins => [ :group_memberships ], :conditions => [ "group_memberships.created_at > ?", Time.now - 30.days ] )

5. 自己寫 SQL

ActiveRecord 可以直接寫 find_by_sql。

6. denormalization 逆正規化

當資料非常多,又要常常查詢其中計算的結果,這時可以考慮使用逆正規化的手法,將計算的結果也當做資料存起來。

一個最常見最基本的用法就是計算總數了,例如以下的例子可以解決需要常常查詢 @topic.posts.size 該篇主題有多少文章的情境:


class Topic < ActiveRecord::Base has_many :posts end class Posts < ActiveRecord::Base belongs_to :topic, :counter_cache => true
end

我們會在 Topic model 新增一個欄位 posts_count,然後 :counter_cache 就會幫我們在新增刪除 Post 時,自動更新所屬 Topic 的 posts_count 欄位資料。這時只要打 @topic.posts.size 就會直接回傳 posts_count,而無需每次都再發一個 SQL query 去 count 有多少筆 posts。

其他逆正規化的方式也包括了統計結算報表等,我們把需要複雜計算的的結果(可以透過非同步的機制如 cron 定期去觸發) 存到 Report model,這樣使用者直接撈 Report 的結果即可。

Rails plugins 投影片

釋出今天在 COSCUP 2008 的投影片,PDF請按這裡下載

Rails Plugins

View SlideShare presentation or Upload your own. (tags: plugin rails)

雖然還蠻走馬看花的,不過20分鐘的 talk 也夠把(我所知道的)常用的 Rails Plugins 都介紹過了一遍,其中也包含了幾個和多自產自用的 open source plugin :)

誠如我投影片所說,熟悉常用的 Rails Plugins 跟熟悉 core 一樣重要。不過投影片沒說的是:Plugin 的地雷可比 core 來的多,因此除了會安裝使用之外,最好也要具備基本 Ruby meta-programming 的能力來大概看懂這些 Plugins 是怎麼做的。

BTW,如果投影片對你有幫助的話,希望可以聽到您的留言鼓勵,或是推薦一下還有什麼好用 Rails plugin :p

誰說人是理性的


好久沒看經濟類的書,這本書從人的非理性層面切入,並用有趣的實驗導出令人驚奇的結果,還蠻有趣的。

  1. 比較才能做決定。提供 A,-A, B 三種選項,其中 A,B 無法比較,A 比 -A 好。實驗出人們會 75% 去選A,即使 A,B 無法比較。這個 -A 的效果稱作誘餌效應。
  2. 定錨點的影響,隨機的定錨點先入為主影響你的決策。
  3. 只要有”免費”這個字眼,就難以抗拒。2塊降價變1塊跟1塊變免費的效果是天差地遠。
  4. 社會規範與市場規範,只要一提到錢,就變成市場規範。(錢不是萬能)
  5. 不同的情緒狀態所做的決定會不同,高張的情緒容易失去理智。
  6. 預先承諾機制可以幫助解決拖延問題
  7. 如果擁有某樣東西,就會對這樣東西估價較高。所有權會讓你的損失趨避心理變得強烈,只想到失去時的損失而忘記會獲得的利益。(由簡入奢易,由奢入簡難)
  8. 人會盡量保留多一點選擇,即使這樣得付出極高的代價。就像父母匆忙帶著小孩到處學才藝,四處奔走不僅讓人緊張,也不符合經濟效益。很多時候在兩個相似的選項中做選擇,猶豫的時間代價比兩者細微的差異損失更多。
  9. 預期心理影響感官認知。但若事後知道內情也不會影響一開始的經驗。
  10. 安慰劑的暗示效果,因為人們相信它有效,所以也出現治療效果。價格因素也會影響效用,貴的比較有效。關於醫療與安慰劑的道德問題是個很大的議題。
  11. 一旦有機會,許多誠實的人都會欺騙,一旦決定作弊,卻不會受被逮到的風險高低所影響,對小的欺騙來說不會思考成本效益分析。有趣的是如果有任何誠實概念進到腦子(例如回憶十戒或簽署榮譽制度),就會容易保持誠實。
  12. 當人們騙的不是現金的時候,欺騙行為較容易發生。甚至即使改成用代幣(多一層轉換手續而已,還是可以換現金),欺騙的比例也會大增。當交易媒介與貨幣無關時,我們就更容易合理化自己的行為。
  13. 行為經濟學了解人容易受到情境、不相關的情緒等不理性因素影響,所以希望利用工具、方法與制度來幫助我們提高決策品質。

我想這本書真正精采的地方在作者設計的這些有趣實驗,作為一本通俗讀物還蠻值得推薦的。不過我在想如果是科普觀點:認知心理學家、生物學家等科學家應該會有更好的解釋為什麼人類會這麼做吧,我自己是認為”人” 的這些非理性決策不是為了找到最佳解,而只是極小化選到最差解的可能 (也許就代表死亡)罷了。

Rails plugins@COSCUP 2008

Update(2008/8/23): 今天的投影片請點此下載

COSCUP

一年一度的 COSCUP 聚會又來了,這次我也投了一場 Rails plugins used/created in Registrano 約二十分鐘的演講。題目顧名思義就是介紹 Registrano 所用到的 Rails open source plugins,就是因為有這麼多好用的開放原始碼插件,才得已讓 Registrano 這個網路服務能夠敏捷地構建出來。

敝社和多這次同時也贊助了活動網站設計,當然還有報名網頁(Registrano)。除了我的 talk,還有 xdite 也會分享 Rails 2.1 的新功能介紹。希望大家多多捧場。

如果您對 Registrano 這項服務有任何想法,屆時也歡迎給予指教,並致贈您 Registrano 折價卷。