顯示具有 Kayako 標籤的文章。 顯示所有文章
顯示具有 Kayako 標籤的文章。 顯示所有文章

2018-01-04

Kayako Cron Tasks Management

傳統 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()。

2017-12-28

Kayako Language Engine and Caching System

先前處理 Kayako email 發送的多國語系問題時,採用新建 LanguageEngine instance,透過 LanguageEngine instance 抓取所需詞彙的方式完成。後來發現,這樣的解法會影響其他信件的發送,例如:ticket auto-close 時的通知信件,會因無法抓到所需詞彙而失敗,追根究柢後發現:無論產生多少新的 LanguageEngine instances,底層實際載入與儲存詞彙都使用相同一塊空間,無法避免動態切換語系時交互影響。

上圖是 LanguageEngine 與 SWIFT caching system 之間的關係。

  1. 首先 LanguageEngine 在載入詞彙時會提供 Laod() 或 LoadQueue() 供外部呼叫,最終都落到 SWIFT 的 caching system,使用 SWIFT_CacheStore->LoadQueue()。
  2. SWIFT_CacheStore 裡頭涉及 2 內部變數:CacheMemory (SWIFT_CacheMemcache) 與 Registry (SWIFT_Registry);因為目前系統並未設定 MEMCACHE_HOST,因此這邊只會使用 Registry 作為實際儲存位置。
  3. SWIFT_Registry 內部的 storage 分為兩層,level 1 的是 in-memory 的 _registryCache,level 2 是 database 的 swregistry table。
這邊要注意:SWIFT_Registry instance 是在 SWIFT 中生成,因此無論每次執行無論處理過多少張 tickets 都會使用相同的 SWIFT_Registry instance,且單一 SWIFT_Registry instance 中,每一個 key 值只會對應到一個 value。

為了避免語系切換時,汙染 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 這套系統超過一年了,一直沒有作單元測試,主要原因有以下兩個:
  1. License 驗證:幾乎所有測試需求都跟底層的 SWIFT framework 有關,而要載入這個 framework instance 的前提是 license 被驗證為有效。
  2. 缺乏文件:雖然它們家的 SWIFT_TestCase 也是繼承自 PHPUnit_Framework_TestCase,但不知如何執行。
然而直接在 UI 端作 acceptance testing 就不用煩惱這個問題,只要修改 client 端的 host 設定,將 license 中的 domain name 綁定測試機 IP。這時候即使測試機上設定其它 hostname,只要 client 端瀏覽器使用 license 的 domain name 連道測試機,就可以正常執行。

最近因為修改的東西愈趨複雜,很多問題無法單純在測試機上透過 acceptance testing 讓問題浮現,往往都是 patch 上正式機才爆出,因此需要回頭徹底解決,步驟上大致如下:
  1. 安裝 PHPUnit;因為 PHP 版本還在 5.4 系列;只能安裝 PHPUnit 4.8.36 [1] [2] [3]
  2. 修改 $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。
  3. 執行範例如下:/usr/local/bin/phpunit.phar --bootstrap ./index.php ./__swift/library/Date/class.SWIFT_DateTest.php
官方預帶了一些 test cases,分別針對 KQL, KQL2, Cron, Date,可以針對需求仿造。

後續可能要再看一下 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。


值得注意的是:
  • Language Engine 透過一個 key-value array 記錄某個 phrase key 對應的字串,其中 phrase key 並不包括語言別資訊,因此一個 engine instance 只能處理一種語言,否則會出現 key conflicts 現象。
  • Template Engine 不涉及語言別的問題,因此系統可共用一個 template engine instance;反之 Language Engine 必須針對需要的產出的語言,生成對應的 instance。
在 Kayako 原本的邏輯中
  • 取用 Language Engine instance 的邏輯被放在 TemplateEngine->Get() 中,除了 Web 操作根據 cookie 在底層 SWIFT 生成不同語言的 instances 外,其餘都使用預設的 (English) Language Engine instance。
  • TemplateEngine->Get() 不帶 phrase keys 參數,Language Engine 負責載入所有的 phrases,交由 Dwoo core 產出最終文本。

因此如果我們要讓非 Web 操作也能產出多語言內容的話,有兩種選擇:
  1. 修改  TemplateEngine->Get(),新增語言別參數,動態產出對應的  Language Engine instance。
  2. 在呼叫  TemplateEngine->Get() 前預先產出所需的 phrases,並於呼叫時傳入。
這邊我採用第 2 種作法,目的在於降低 Language Engine 載入的 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,這邊可以直接取用,不需要另行初始化。