傳統 web application 實作 cron jobs 後,驅動源頭大多來自 web container 或作業系統 crontab 設定,由 application 外部負責定期啟動內部的 cron task manager,看看是否有哪個 cron task 該被執行。
Kayako 在 cron task manager 以下的實作跟一般作法沒有什麼不同,比較特別的是:它把 cron task manager 暴露成一個 Web API (https://site/cron/index.php?/Base/CronManager/Execute) 讓瀏覽器直接執行,而不仰賴 web container 或作業系統設定。
至於執行的時機與頻率,Kayako 把這段邏輯包在 https://site/Core/Default/Compressor/js 中,讓每個頁面都 include 這段 JavaScript,也就是當系統任何一個畫面被瀏覽器訪問時,都會同時執行一次內部的 cron task manager。
Cron task manager 的 Web API 先由 Controller_CronManager 處裡,接著進入真正的 SWIFT_CronManager::RunPendingTasks()。
2018-01-04
2017-12-28
Kayako Language Engine and Caching System
先前處理 Kayako email 發送的多國語系問題時,採用新建 LanguageEngine instance,透過 LanguageEngine instance 抓取所需詞彙的方式完成。後來發現,這樣的解法會影響其他信件的發送,例如:ticket auto-close 時的通知信件,會因無法抓到所需詞彙而失敗,追根究柢後發現:無論產生多少新的 LanguageEngine instances,底層實際載入與儲存詞彙都使用相同一塊空間,無法避免動態切換語系時交互影響。
上圖是 LanguageEngine 與 SWIFT caching system 之間的關係。
為了避免語系切換時,汙染 SWIFT_Registry instance 中的語言詞彙,因此決定改採直接 SQL query 的方式載入所需語言。
ps. 這邊的 caching system 指的是 data cache,Kayako 在 4.0 剛推出時並沒有針對 Memcached 整合的功能;倒是官方推薦使用針對 code cache 的 XCache [1]。相較於新版 PHP (>5.5) 更被推崇的 code caching system 是 opCache [2]。
see also:
[1] Kayako Forum: V4 Very Slow!
[2] StackExchange: PHP 7 opCache v PHP 5.6 XCache
上圖是 LanguageEngine 與 SWIFT caching system 之間的關係。
- 首先 LanguageEngine 在載入詞彙時會提供 Laod() 或 LoadQueue() 供外部呼叫,最終都落到 SWIFT 的 caching system,使用 SWIFT_CacheStore->LoadQueue()。
- SWIFT_CacheStore 裡頭涉及 2 內部變數:CacheMemory (SWIFT_CacheMemcache) 與 Registry (SWIFT_Registry);因為目前系統並未設定 MEMCACHE_HOST,因此這邊只會使用 Registry 作為實際儲存位置。
- SWIFT_Registry 內部的 storage 分為兩層,level 1 的是 in-memory 的 _registryCache,level 2 是 database 的 swregistry table。
為了避免語系切換時,汙染 SWIFT_Registry instance 中的語言詞彙,因此決定改採直接 SQL query 的方式載入所需語言。
ps. 這邊的 caching system 指的是 data cache,Kayako 在 4.0 剛推出時並沒有針對 Memcached 整合的功能;倒是官方推薦使用針對 code cache 的 XCache [1]。相較於新版 PHP (>5.5) 更被推崇的 code caching system 是 opCache [2]。
see also:
[1] Kayako Forum: V4 Very Slow!
[2] StackExchange: PHP 7 opCache v PHP 5.6 XCache
2017-12-08
Unit Test for Kayako
接手 Kayako Fusion 這套系統超過一年了,一直沒有作單元測試,主要原因有以下兩個:
最近因為修改的東西愈趨複雜,很多問題無法單純在測試機上透過 acceptance testing 讓問題浮現,往往都是 patch 上正式機才爆出,因此需要回頭徹底解決,步驟上大致如下:
後續可能要再看一下 mock object 與相關 inject testing database 的方法,這樣就可以把單元測試這塊補上。
see also:
[1] PHPUnit
[2] PHPUnit Download
[3] PHPUnit 4.8 Manual (PDF)
[4] PHP.NET: $_SERVER
[5] StackOverflow: $_SERVER['HTTP_HOST'] not set
[6] Kayako Classic User Guide
[7] Kayako Developer Resources
- License 驗證:幾乎所有測試需求都跟底層的 SWIFT framework 有關,而要載入這個 framework instance 的前提是 license 被驗證為有效。
- 缺乏文件:雖然它們家的 SWIFT_TestCase 也是繼承自 PHPUnit_Framework_TestCase,但不知如何執行。
最近因為修改的東西愈趨複雜,很多問題無法單純在測試機上透過 acceptance testing 讓問題浮現,往往都是 patch 上正式機才爆出,因此需要回頭徹底解決,步驟上大致如下:
- 安裝 PHPUnit;因為 PHP 版本還在 5.4 系列;只能安裝 PHPUnit 4.8.36 [1] [2] [3]。
- 修改 $KAYAKO_HOME/tests/index.php;因為 license 檢驗,會比對 license 中的 domains 與 $_SERVER['HTTP_HOST'],而在 UI 端作 acceptance testing 時,$_SERVER['HTTP_HOST'] 來自 HTTP request header 中的設定 [4] [5],改執行 unit testing 時缺少真正的 request header,因此要在 index.php 中自行設定,並作為執行時的 bootstrap script。
- 執行範例如下:/usr/local/bin/phpunit.phar --bootstrap ./index.php ./__swift/library/Date/class.SWIFT_DateTest.php
後續可能要再看一下 mock object 與相關 inject testing database 的方法,這樣就可以把單元測試這塊補上。
see also:
[1] PHPUnit
[2] PHPUnit Download
[3] PHPUnit 4.8 Manual (PDF)
[4] PHP.NET: $_SERVER
[5] StackOverflow: $_SERVER['HTTP_HOST'] not set
[6] Kayako Classic User Guide
[7] Kayako Developer Resources
2017-09-28
Kayako Template & Language Engine
Kayako Fusion 是一套 on-premise helpdesk 系統,使用的語言為 PHP,framework 為自有的 SWIFT framework,在修改或整合上會需要一些功夫去瞭解系統架構,才不至於出現難以維護或資料不一致的現象。最近處理了一段有關多國語系的問題,這邊順便將心得記錄一下。
首先,Kayako 中提供了兩個元件來處理多國語系:Template Engine 與 Language Engine,分別負責處理文本的模版 (template) 跟各語言詞彙 (phrase),資料來源都同時支援 database 與 file system, 下圖說明這兩個元件的關係與 data flow。
值得注意的是:
因此如果我們要讓非 Web 操作也能產出多語言內容的話,有兩種選擇:
假設今天我要製作一個具備多國語系的畫面,除了預先將 template 跟 phrase 準備好存入 database 或 file system 外,主要的 rendering 邏輯如下:
首先,Kayako 中提供了兩個元件來處理多國語系:Template Engine 與 Language Engine,分別負責處理文本的模版 (template) 跟各語言詞彙 (phrase),資料來源都同時支援 database 與 file system, 下圖說明這兩個元件的關係與 data flow。
值得注意的是:
- Language Engine 透過一個 key-value array 記錄某個 phrase key 對應的字串,其中 phrase key 並不包括語言別資訊,因此一個 engine instance 只能處理一種語言,否則會出現 key conflicts 現象。
- Template Engine 不涉及語言別的問題,因此系統可共用一個 template engine instance;反之 Language Engine 必須針對需要的產出的語言,生成對應的 instance。
- 取用 Language Engine instance 的邏輯被放在 TemplateEngine->Get() 中,除了 Web 操作根據 cookie 在底層 SWIFT 生成不同語言的 instances 外,其餘都使用預設的 (English) Language Engine instance。
- TemplateEngine->Get() 不帶 phrase keys 參數,Language Engine 負責載入所有的 phrases,交由 Dwoo core 產出最終文本。
因此如果我們要讓非 Web 操作也能產出多語言內容的話,有兩種選擇:
- 修改 TemplateEngine->Get(),新增語言別參數,動態產出對應的 Language Engine instance。
- 在呼叫 TemplateEngine->Get() 前預先產出所需的 phrases,並於呼叫時傳入。
假設今天我要製作一個具備多國語系的畫面,除了預先將 template 跟 phrase 準備好存入 database 或 file system 外,主要的 rendering 邏輯如下:
- 根據需要的語言別,初始化 Language Engine instance。
- 根據 phrase keys 與 language id (or language code) 透過 Language Engine 載入 phrases。
- 呼叫 Template Engine 中的 Get(),傳入 template key 與 phrases,生成 output content;其中 template engine 會在 SWIFT_Base 中初始化,如果已繼承 SWIFT MVC classes 或 SWIFT_Library,這邊可以直接取用,不需要另行初始化。
訂閱:
文章 (Atom)