2010年10月25日 星期一

lsof: list open files

剛才要重裝 nose 卻一直跑出這樣的錯誤訊息:
OSError: [Errno 16] Device or resource busy: '/some-path/lib/python2.5/site-packages/nose/plugins/.nfs0000000001d8a00f000005c0'

查到這篇說是 NFS 的安全措施, 可以用 lsof 檢查那些 process 開啟目標檔案。用 lsof 找出相關 process, 將它們清光後就 OK 啦。

2010年10月23日 星期六

avi 轉 flv和播放 flv 的方法

經同事 R 和同事 M 指點後, 順利地做好這件事, 備忘一下。

名詞解釋

  • flv: flash video 格式, 比 avi 小很多。網路上的影片多為 flv 格式。
  • swf: flash 執行檔? 能播放動畫或執行程式。

avi 轉 flv

我不清楚別人交給我的 avi 檔是什麼格式 (印象中 MS 的 avi 有不同種類?), 我試了三四個軟體, 最後用 SUPER © 轉成功了。不過這軟體超難下載的, 而且參數太多令人無所適從, 幸好 《好用的影音轉檔軟體--SUPER ©》 解釋的相當清楚。照著網站步驟下載軟體, 照文章內捷圖選參數, 看起來還算 OK。

播放 flv

要用網頁載入 swf, 再透過 swf 播放 flv。依 Wikipedia 所言, 99% 電腦能執行 swf 檔, 應該不用擔心不能播 swf 的問題。

FLV Player 官網下載 FLVPlayer.swf, 再依官網提供的工具產生對應的 html code。下面是一個簡化的範例:
<object type="application/x-shockwave-flash" width="400" height="350"
        wmode="transparent" data="FLVPlayer.swf?file=X.flv&autoStart=true">
    <param name="movie" value="FLVPlayer.swf?file=X.flv&autoStart=true" />
    <param name="wmode" value="transparent" />
</object>

將上文的 X 改成你放 flv 檔的位置, 還有確定 FLVPlayer.swf 有和網頁放到同樣位置即可。

2010年10月19日 星期二

小心瓶頸就在資安軟體

這是一個很囧的心得。

今天下午發覺有個簡單的操作竟然有時候要花一秒鐘, 做了許多交叉比對, 發覺以下驚人的跡象:
  • 同一份程式、同一份資料庫, 連用 apache 跑的服務時, 有時要花一秒; 但連 django development server 時卻是瞬殺。
  • log 顯示, 不管連那種 web server, 我的程式都只花不到 0.05s。
  • 用 Chrome Developer Tool 的 Resource 觀看, 慢的時候, 大部份時間是 waiting。
  • 用 VNC 連到 server, 再從本機連 apache, 也是瞬殺。
  • 從我的電腦或同事的電腦連 apache 都是有時會延遲, 我的情況比較頻繁。
  • 同樣的連結點第二次時會變快, 但過一陣子再點似乎又會變慢。
  • 我主要是測 AJAX + GET。
綜合以上線索, 我下了一個很腦殘的推論: apache 有鬼!! 不知是 apache 讀檔案還是做些奇怪的事, 或是 mod_wsgi 做奇怪的事, 造成延遲。也許 apache 有奇妙的 cache, 造成同一連結點第二次時會變快。
和來和同事 R 討論後, 他問了一個問題: 「該不會是是資安軟體阻擋吧?」 (*1) 於是同事 P 在 server 隨便開個奇怪的 port, 將該 port 的封包轉到本機的 port 80。我再試的結果, 速度就變正常了!!
事後回顧發覺自己的推論太腦殘了, apache + 80 和 django development server + some port, 我卻忽略了 port 的事, 只看到 web server 的差異。差點要去找 apache 或 mod_wsgi profiling 的方法, 幸好有先和同事閒聊, 省了一堆工, 也放下心裡一塊大石頭。

註 *1: 資安軟體會攔截對外連的 URL, 阻止連某些網站。沒想到連對內也有攔截.....。

2010年10月18日 星期一

profile Django 的方法 + 關掉 I18N 減少 template rendering 的時間

今天發現 template rendering 吃掉不少時間, views 和 db 等操作還可以自己寫個簡單的 profile function 記錄, template 就不方便啦。查了一下, 看到 "Speeding up built in Django Templates" 提到, 可以參考 "Profiling Django" 的說明 profile 全部東西, 該篇有提到用 django-command-extensions 的 runprofileserver 來 profile Django development server, 試了一下, 真不錯用, 之後再來試試 WSGI 的部份。

profile 的結果, 和作者差不多, 卡在 I18N 做了一堆工, 我不確定 stringformat 是否有效, 至少我胡亂在很多地方加上去沒啥效果, 後來直接在 settings.py 裡將 I18N 設為 False, template rendering 的時間就從 0.4s 降到 0.25s。不過 django/utils/importlib.py:18(import_module)、django/utils/translation/__init__.py:23(delayed_loader)、django/utils/formats.py:10(get_format_modules) 還是占了一堆時間。程式碼看起來像只會執行一次的東西, Django runserver 大概是每次都重讀全部 modules 才會吃這麼多時間吧, 之後再來看是否用 WSGI 時也會如此。

Ubuntu 安裝 mtop 的問題

裝一些相關 package 時, 會炸在安裝 mtop 的部份, 錯誤訊息如下:
Setting up mtop (0.6.6-1.1) ...
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)
dpkg: error processing mtop (--configure):
 subprocess post-installation script returned error exit status 1
Errors were encountered while processing:
 mtop

查了一下才發覺真算是 bug (我用 8.04, 不過其它版也有這問題), 有一個很爛的解法, 就是先將 mysql root 密碼設成空字串, 裝完再設回來......。做法如連結裡寫的, 用有權限的帳號進 mysql client 後, 打:
SET PASSWORD FOR root@localhost=PASSWORD('');
再執行 "aptitude install mtop", 就搞定啦。裝完記得馬上設回 mysql root password。

2010年10月17日 星期日

Ubuntu 上 mysql 參考設定檔的位置

apt-file 真是不錯用, 用它找到 my-huge.cnf 的位置:
$ apt-file search my-huge.cnf
mysql-server-5.0: /usr/share/doc/mysql-server-5.0/examples/my-huge.cnf.gz
mysql 有提供幾組預設設定檔, 以現今電腦的設備, my-huge.cnf 假設的規格都算小吧, 更別提系統裝好的預設值。

2010年10月12日 星期二

讓 Firefox 在同一網站同時登錄多個帳號

目前我有兩個情況會有這種需求:
  • 同一網站, 要測不同使用者互動。
  • 同一個 domain, 同時在不同 port 跑不同版網頁, 需要同時登錄比較差異, 但 cookie 是以 URL 不含 port 當 key, 所以無法同時登錄不同 port 的網頁。
平時我用一個 FF、一個 Chrome 外加一個 Chrome 無痕式視窗搞定, 但最近 javascript 有 bug, 在修好它以前, 相當於廢掉兩個登入額度。只好回頭看看怎麼用 FF 同時登入。
眾人指點後, 查了一下資料, 找到解法:
  1. 關掉 firefox, 改用「開始」-> 「執行」, 打入 "firefox -P" 啟動 profile manager
  2. 建立另外一個 profile "dev", 關掉 profile manager
  3. 執行指令 "firefox -P default -no-remote"
  4. 執行指令 "firefox -P dev -no-remote" , 這樣就同時有兩個 FF 互不干擾
新弄的 dev 就和剛裝好的 FF 一樣, 設定都是預設值, plugin 是空的。可以用 FEBE 輕鬆重裝原有的 plugin, 或是用 MozBackup 「還原」 profile, 將原有設定複製到新 profile。我還沒試過就是了, 我改的東西很少, 在新的 profile 裡重裝 FireBug 方便開發就夠了。其它的用原有 profile 執行的 FF 即可。

2010年10月11日 星期一

簡單的 mock 使用情境

今天在寫新功能時, 又陷入小小的苦惱。我要寫一個小函式, 這個函式會依參數從資料庫取出資料, 並依參數決定做那些操作, 再傳回操作完的結果。函式本身需求明確不複雜, 但有不少組合情況要處理好, 有 unit test 比較保險。

在開始 TDD 前, 我得先決定如何準備 fixture (輸入資料)。大概有兩種選擇:
  1. 規劃好目標函式的測試情境, 準備測試用的資料庫, 塞對應的資料進去。
  2. 使用 Mock (我用 pymox)
配合 Django TestCase 和 ORM 操作, 塞入測試資料已簡單許多, 不用自己產生一個同 schema 空的資料庫, 並在每個 test case 前先清空資料, 也不用自己下 SQL 填資料, 所以實作門檻已低了不少。但是仍有幾個問題:
  1. 當測試資料有 relation 時, 要多塞不少相關資料。
  2. 要規劃清楚那些案例需要那些資料。規劃完後, 也需要寫註解畫個表格之類的, 不然很難了解測試資料內容。即使畫了表格, 也不太直覺易懂。
  3. 日後發現新 bug 或改功能時, 不容易修改這堆測試資料。
最讓我受挫的是, 寫完後很難閱讀和維護, 測試案例一多, 根本沒興趣慢慢讀輸入資料的細節。其它像是和外部資源綁在一起、實作目標函式時得先寫相依函式等議題, 在這個例子裡到是沒那麼嚴重。
這回我改試著用 Mock, 將目標函式的介面寫成大概如下的形式:
def target_function(arg1, arg2, getter=db.some_function):
    ...
db 是我另開的模組, 用來放包著 SQL 操作的函式。getter 指向真正用的函式, 測試時則視測試案例傳不同的 mock objects。
使用 Mock 的話, 準備測試資料變得很容易, 而且直接寫明目標函式如何和這些資料互動, 不用看資料庫的資料想像它轉換後的資料。要加測試案例時簡單許多, 不用思考如何在不同表格組出前置資料, 再用這些資料經由資料庫操作轉成中間資料給目標函式用。整體評估下來, 為了測試而多加參數 getter 是很划算的設計, 在註解裡註明一下 getter 的用意即可。

用 Mock 帶來的問題則是:
  1. 視使用的方式, 對目標函式的實作細節需有不同深度的理解。像是 getter 被呼叫幾次, 傳入那些參數。若程式有部份是別人寫的, 需要另外時間讀原本已被隱藏的細節。 
  2. 目標函式改變使用 getter 的方式時, 也需更新準備 mock 的測試程式。
  3. 無法保證目標函式正確, 實際使用目標函式時會有正確成果, 因為 getter 有可能出錯。導致有可能要另測 getter。相對來說, 直接存取資料庫的例子, 不測 getter 的風險較小。
換個角度來看, 測試程式使用 Mock 相當於切開和資料庫的連繫, 卻增加和 getter 操作的連繫。

2010年10月8日 星期五

django template tag 傳參數 + 使用 template 的方法

之前眼殘一直沒看到, 還埋怨 Django template 不人性。上 news groups 查了一下相關問題, 發覺都沒有人有這種困擾, 又回頭看文件, 才發現自己眼殘。

template tag 有兩種模式, 一種是先 parse, 產生 node, 再 render 回傳字串。雖然語法較彈性, 用起來有點複雜, 而且在 python code 裡產生 html 字串, 而不是在 template 裡嵌入變數, 較不容易維護。 另一種用法是 inclusions tag, 就和用 template 的 include 差不多, 不過變數的來源有兩種。

一種是用傳參數的, 這正是我需要的, 抽出重覆的 template codes, 傳變數用在不同地方。另一種是不接收參數, 直接用 context 當全域變數取出 template 內的東西。但這樣就無法在一個 template 裡使用多次。

2010年10月2日 星期六

MySQL varchar 長度和效能問題

看《High Performance MySQL 2e》 p242 才發現, 可怕的效能問題。varchar 在 disk 上只存必要的空間, 但丟到記憶體後卻當作 char 來存。MySQL 內部處理字串操作時, 是用固定大小的 buffer, 所以 utf-8 會強迫占用 3 bytes, char(10) 會占用 30 bytes。雖然 varchar 在硬碟上只存必要的空間, 但在記憶體裡卻會被當作 char 看待。原文如下:

MySQL uses fixed-size buffers internally for many string operations, so it must allocate enough space to accommodate the maximum possible lenght. For example, a CHAR(10) encoded with UTF-8 requires 30 bytes to store, even if the actual string contains no so-called wide characters. Variable-length fields (VARCHAR, TEXT) do not suffer from this on disk, but in-memory temporary tables used for processing and sorting queries will always allocate the maximum length needed.

MEMORY engine 用 varchar 等同於 char, MySQL 暫存表格似乎是用 MEMORY engine, 無法用 MEMORY engine 時會改用 MyISAM 存在硬碟上。若有某個 column 或 select 結果用超過 512 bytes, 該 table 或 select 結果不能存在 memory 裡。

綜合以上所言, 結果就是 varchar + utf-8 長度設超過 170 的話, 就無法存在 memory 裡。別以為用 varchar 就可以亂設最長長度啊。

另外, 即使 varchar 可以設長度到 65536, index 時只能取前 1000 bytes (不是 chars), varchar 過長時, 建 index 會出現錯誤, 而要求明確指示 index length。若自己建 index 到也還好, 但用 Django 還是 South 時會噴掉, 沒辦法自動產生正確的 SQL 建過長 varchar 的 index。

補充一下不能存在 memory 裡有多糟, 這表示 SQL 有用到 order by 或 group by 時, MySQL 得不斷用硬碟暫存結果。以 order by 為例, 假設有 column A, B, 其中 A 能放在 memory, B 不行。即使用 column A 排序, 排完後還得一筆筆依排序結果的順序從硬碟取出 column B 的資料, 那會是非常的慢。詳細的說明見《ORDER BY Optimization》裡 "The original filesort" 和  "The modified filesort" 的說明。

在 Fedora 下裝 id-utils

Fedora 似乎因為執行檔撞名,而沒有提供 id-utils 的套件 ,但這是使用 gj 的必要套件,只好自己編。從官網抓好 tarball ,解開來編譯 (./configure && make)就是了。 但編譯後會遇到錯誤: ./stdio.h:10...