2013年9月27日 星期五

Rails 的 Counter Cache 機制

雖然很基本,但是一定要做一下記錄。
我們先想像一個情況:你目前的專案中,有user及comment兩種model:

class User < ActiveRecord::Base
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :user
end

如果在一個頁面需要將使用者列表,並同時顯示他所擁有的回應數時,我們直覺上會在迴圈中,一個個進行user.comments.count,並同時產生一個db query,這樣的情況就是經典的「N+1」問題。


讓我們把counter cache住吧!

在系統架構上還滿常用到的一個手法是,將「讀取機會比寫入機會大很多」的數值記錄起來,例如這次會講到的counter cache,就是乾脆在user資料表上開一個comments_count欄位,在新增或刪除comment的同時,記錄他目前所擁有的comment數。
如果你對rails有一定程度的了解,應該會想到使用ActiveRecord callback的機制在新增或刪除comment後進行user.comments_count的更新。但是也因為這是一個還算常見的作法,Rails本身就內建了這個功能,能夠讓我們所做的工減少很多。


進入正題

1. 新增counter欄位

首先我們要先在user資料表中開一個專門儲存comment數的欄位,由於rails「Convention over configuration」的核心精神,這個欄位應該被命名為 [resource複數]_count ,否則rails可能會認不得他。以這個例子來說,就是要新增一個叫 comments_count 的欄位,並且預設值為0。新增欄位的過程就不贅述了。


2. 修改relation選項

接下來要做的是在comment.rb中的relation上加上一個選項::counter_cache ,告知Rails這個relation是要做counter的同步動作的。程式碼變成:

class Comment < ActiveRecord::Base
  belongs_to :user, :counter_cache => true
end

如果目前的專案增加這個欄位時,並沒有存在任何資料,也就是所有的user所擁有的comment是0,這樣的情況下其實這樣已經ok了。但如果之前就已經有一些user有創造comment,這時我們就要做counter的更新。


更新counter cache

可能有些人會想說,直接遍尋所有user,讓他們的 comments_count = user.comments.count 就好。
很不幸的,Rails對於counter欄位有保護機制,我們不能這樣直接修改他,而必須使用 update_counters(id, counters) 或 reset_counters(id, *counters) 兩種方法,以reset_counter來說,migration的程式碼就像這樣:

User.find_each do |user|
  User.reset_counters user.id, :comments
end



PostgreSQL 新增使用者及設定「唯讀」權限

新增user

以下是新增使用者之指令及相關選項:

Usage:
  createuser [OPTION]... [ROLENAME]

Options:
  -c, --connection-limit=N  connection limit for role (default: no limit)
  -d, --createdb            role can create new databases
  -D, --no-createdb         role cannot create databases
  -e, --echo                show the commands being sent to the server
  -E, --encrypted           encrypt stored password
  -i, --inherit             role inherits privileges of roles it is a
                            member of (default)
  -I, --no-inherit          role does not inherit privileges
  -l, --login               role can login (default)
  -L, --no-login            role cannot login
  -N, --unencrypted         do not encrypt stored password
  -P, --pwprompt            assign a password to new role
  -r, --createrole          role can create new roles
  -R, --no-createrole       role cannot create roles
  -s, --superuser           role will be superuser
  -S, --no-superuser        role will not be superuser
  --help                    show this help, then exit
  --version                 output version information, then exit

Connection options:
  -h, --host=HOSTNAME       database server host or socket directory
  -p, --port=PORT           database server port
  -U, --username=USERNAME   user name to connect as (not the one to create)
  -w, --no-password         never prompt for password
  -W, --password            force password prompt

If one of -d, -D, -r, -R, -s, -S, and ROLENAME is not specified, you will
be prompted interactively.

其中比較重要的是 -P 這個選項,在建立使用者的同時為他設定密碼。如最後一行(紅字)提到的,「創建DB」(d)、「創建user」(r)、「是否為最高權限」(s)。


為使用者設定唯讀權限


在postgres中,並沒有所謂的「唯獨權限」,所以我們必須透過修改使用者的預設參數來達到這個目的:

alter role digoal set default_transaction_read_only=true;



開放 Tables 和 Sequences 權限給使用者


特定 Table:
GRANT SELECT ON table_name TO role_name;

所有 Tables:
GRANT SELECT ON ALL TABLES IN SCHEMA schema_name(通常是public) TO role_name;

所有 Sequences:
GRANT SELECT ON ALL SEQUENCES IN SCHEMA schema_name TO role_name;

Reference

德哥@Digoal的博格:http://blog.163.com/digoal@126/blog/static/1638770402011111274336235/

2013年9月21日 星期六

Ajax跨域請求 - jQuery.getJSON

從事前端開發的工程師應該或多或少都知道ajax無法做跨域請求,除了JSONP這種形式以外:
$.getJSON( 
    "http://localhost:3030/parse?callback=resHandler",
    { q: queryText }
);

但是這樣的用法是不會觸發success callback的,因為從server side回傳的資料會被當做script直接執行,假設從server side回傳的訊息為:
{
    name: "kevin",
    age: 18
}

如此一來在收到這個訊息後會直接執行這段程式碼,但是因為這段訊息並不遵守javascript的文法,因此會fail掉。聰明的程序猿一定知道,我們必須在server side回傳的資料上加點料,讓我們要的資料能適合的函式處理。
從第一段程式碼中看到,我們在請求的url上加了一個callback參數,通知server side 我們要用什麼函式來對資料做處理,假如這段函式叫resHandler好了,我們期待的運算是這樣:
resHandler(data);

data為server應該要回傳的資料。所以在server side我們就應該要這樣處理:
var data = '{msg: "success!"}' //主機回傳資料
var callbackFunction = req.query.callback;

res.send(callbackFunction+"("+")");

這時client端接收到這段資訊後會直接執行他:
resHandler({msg: "success!"})

假如resHandeler為 function(data){alert(data.msg);} ,user便會看到視窗跳出"success"的提示。

2013年9月8日 星期日

還22k一個清白吧,請先檢視自己的價值及定位

聽到太多人罵22k,到底22k是怎麼來的?


「大專畢業生企業實習方案」,俗稱22k方案

當初政府在推行這個政策的心態是好的,教育部補助26,190元讓企業聘請「實習生」,扣除勞健保後,實習生實領22k,這也是22k的由來。
這個政策的推出使得了三萬八千人能夠投入職場,而即便這個方案結束了,當年的38,000人中也有六成五的留職率。

但如果沒有22k方案,那些人能這麼順利找到工作嗎?

老實說當年的政策讓受惠者能得到「寶貴的第一份工作」,有了經驗後總比白紙一張好找工作。所以我覺得這個政策本質上是利大於敝的。


22k只是個數字罷了

當然有人說,從此以後22k成了企業的標準薪資。這可能是當初政策所造成的定錨效應沒錯,所以很多人看到這個數字就「高潮」了。但是問題真的不在這個數字,而是供需法則。當大家通通爭著讀大學、研究所,甚至博士班,但相應的職缺又沒有增加。所以大家只好開始往待遇相對較差的職位找工作,然後就有「你看隔壁的那個小孩唸到碩士班結果薪水還不到四萬,你大學畢業後一定要念個研究所不然一定活不下去」這種說法。

看到同儕讀研究所,於是也跟著讀研究所,發現碩士供過於求於是又讀博士,然後跟弟弟妹妹說至少要讀個研究所不然找不到工作.....於是就進入了惡性循環,大家永遠在跟風、讀書、嚇自己。


改變心態吧,教育不是為了讓你找個好工作,而是希望你適性發展

在台灣,高等教育是分一般、技職、跟師範體系的,為的就是因材施教。但大家往往對技職體系不屑一顧,搞到最後技職體系也沒守住貞操,紛紛改稱「科技大學」,放棄了最重要的技職訓練。搞到最後大家讀出來都一樣,科大出來的不會修水電不會車床不會修馬達,也不想進生產現場學習,人人都想坐辦公室。
但退一百步來說,其實這樣也ok,因為這是你的選擇,喜歡讀書就讀吧~但是從什麼時候開始你有了「讀好書就可以拿高薪」的錯覺?現在的文憑很貴,但對職場來說一點都不值錢,為什麼?因為「好學校」是教你怎麼讀書,而不是怎麼「和職場接軌」
其實當初政府希望教育部去推動這個22k專案時,很多學校是覺得很莫名其妙的,「我們的目的是要『作育英才』,這乾我屁事?」
所以這也是為什麼我覺得大家要從心態開始改變,認真考慮自己為了什麼讀書。大家都想大學畢業後坐辦公室,才符合自己所受的「高等教育」,每月看到薪水條上的數字後就大嘆學歷不值錢。但是偏偏自己的工作可替代性太高,你不做還有一堆人等著做,「唉,還是待著吧」。
相較之下,很多藍領的薪水不知道比22k高多少了,更別提三到五年的「老手」,薪水可能隨便都是22k的3~4倍。到你家修馬桶、冷氣的可能比你的薪水還要高。

嫌薪水低?承認吧,你只是不想把手弄髒而已。


假設你是老闆,你會.....?

案例一

如果我今天要雇一個應屆畢業生當工程師,一樣的錢我寧願雇學士,而不是碩士甚至博士生。Why?很簡單,因為學校絕對沒教我需要的技能,反正我還是要從頭訓練,學士、碩士、博士對我來說有差嗎?

真的沒差嗎?ㄟ.....讓我想想......

有!
一,大學畢業的比碩士少操兩年、比博士少操2+n年,別講熱情,光講體力就比你不知道好多少。
二,一樣的薪資,「學士感激、碩士無感、博士嫌少」。

所以你說我幹嘛不請個學士就好?


案例二

如果你當了老闆,今天要找一個行政助理,門檻超低。現場有10位畢業生搶著應徵,學歷都差不多,私立xx大學xx管理系之類的,個性或積極度也看不出有什麼不同,而期望待遇從2萬到4萬都有,你會願意花多少錢?


結論勒?

  1. 問題不會全都在政府,如果你自覺被虧待,也請好好檢討一下自己
  2. 如果你真的覺得賺錢最重要,那別急著念研究所,多賺兩年的薪水再加上年資絕對比得上一個碩士學位
  3. 想賺錢切忌眼高手低
  4. 如果你真的覺得辦公室就是你的歸屬,而且念了一堆書也拿不出什麼可以說嘴的專長,薪水的部份就別太要求了吧