iT邦幫忙

2025 iThome 鐵人賽

DAY 22
0
佛心分享-SideProject30

Mongory:打造跨語言、高效能的萬用查詢引擎系列 第 23

Day 22:Mongory Bridge 的下一站:為什麼選 Go

  • 分享至 

  • xImage
  •  

Ruby 這一季的橋接已經證明可行:讀者能用 Mongory 的 DSL 操作任意 Ruby 結構,性能也在一連串優化後到位。接下來,筆者把目光放到 Go。這不是一時興起,而是「工程現場」與「使用者規模」共同推動的選擇


為什麼是 Go:工程現場與市場補位

  • 大量後端與平台團隊使用 Go:單一靜態可執行檔、部屬簡單、標準庫齊全
  • Mongory 的優勢(記憶體友好、可觀測的 matcher tree、精確的比較語意)很適合 Go 的微服務世界:在單機上做高效率的條件判斷、過濾與聚合
  • 讀者期待在 Go 裡也能以「MongoDB 風格的 DSL」重用既有心智模型,這正是 Mongory 的價值主張

語言特性與橋接難度(認清代價)

  • cgo 能力充足,但有必要的限制:
    • 指標安全:Go 物件指標不可任意長期保存於 C,跨界需遵守 cgo 規則
    • 例外模型不同:Go panic 與 C 的 error 機制不通用,需明確轉譯
    • 執行緒與排程:Go runtime 管理 goroutine 與系統緒,呼叫 C 會有 GVL 類似的切換成本(本質不同,但同樣需要重視)
  • 工具鏈與分發:
    • 模組系統不會幫讀者自動抓子模組的 C 原始碼,若依賴外部 repo 的子模組,讀者 go get 會失手
    • 交叉編譯下的 cgo 需求繁瑣,維持簡單可用的路線,是設計上的優先級

結論:Go 可以橋接,但要維持「對讀者無感」的體驗,工程上需要另闢蹊徑


我們的選擇:先 cgo 快速橋接,再評估 native Go 重寫

  • 第一階段:以 cgo 直接橋接 Mongory-core(C),盡量重用既有的匹配引擎與記憶體池
  • 第二階段:觀察 cgo 的性能與可維運性,逐步抽換為 native Go 實作(預期在大量資料與高呼叫頻率下收益更明顯)
  • 始終維持 API 的穩定與可預期:讓讀者不因底層替換而改動程式碼

實作現況:讓 go get 不踩子模組的坑

現況的 mongory-go/ 採「嵌入核心程式碼快照」的策略:

  • mongory-go/mongory-core/ 內納入核心 C 程式碼與標頭,避免讀者 go get 時因 submodule 缺失而無法建置
  • mongory-go/cgo/ 提供橋接:
    • cgo/binding/include/cgo/binding/src/ 對應核心的公開標頭與來源
    • *.go 封裝 matcher、value、memory pool 的最小可用 API
  • 維運面以同步腳本將核心更新到 mongory-go/mongory-core/(維護者動作,讀者無需介入)

讀者角度只需知道:Go 版會內含建置所需的 C 原始碼與標頭,不需要讀者額外準備 core 子模組


預期與風險:先講白,再上路

  • cgo 呼叫成本:
    • 單次呼叫固定開銷高於純 Go,需盡量以「批次/較粗粒度」的 API 設計減少跨界次數
    • 以 shallow 包裝策略延遲資料展開,降低複製與 GC 壓力(對齊 Ruby 版的成功經驗)
  • 記憶體生命週期:
    • C 的 memory pool 與 Go GC 的協同需嚴謹規範,避免懸空指標與雙重釋放
    • 僅以 C 管理的物件不得持有 Go 指標(遵循 cgo 規範)
  • 發佈與相容:
    • 早期版本可能需要讀者環境具備 C 編譯器(CGO_ENABLED=1),後續會在 CI 端提供更友善的建置體驗

這些風險可控,而且有 Ruby 版的成熟設計可複用:memory pool、value wrapper、matchers 與 explain/trace 的邏輯均已成型


選型取捨:API 與資料轉換的邊界

  • 轉換策略延續 Ruby 的三段式:shallow/deep/recover
    • shallow:O(1) 包裝 Go 容器元素,按需展開
    • deep:完整展開,用於條件與常值
    • recover:將匹配結果回轉為 Go 端型別
  • Adapter 邊界:仍把 Regex 與自訂 matcher 的行為外包給宿主語言(本例為 Go),C Core 專注於匹配骨架
  • API 設計:
    • 以「一次建立 matcher,重複匹配多筆資料」為核心,避免重複建構與跨界呼叫
    • 提供 explain/trace 以維持可觀測性,便於調參與排錯

路線圖(Go 版)

  • v0.x(橋接雛形):
    • cgo 對接 Mongory-core,提供最小可用的 matcher 與 explain
    • 完成 shallow 包裝與基本容器取值
  • v0.y(性能與體驗):
    • 降低 cgo 邊界呼叫頻率,補齊 trace 與彩色輸出
    • CI 補強多平台建置與驗證
  • v1.0(目標):
    • 視成效抽換為 native Go 關鍵熱路徑,或維持 cgo 方案但達到穩定 SLA

筆者會在後續篇章逐步分享:cgo 與子模組同步、反射與 converter 邊界、Go ↔ C 指標安全、Go GC × memory pool 協作、shock 與優化歷程,最後再談為何考慮 native Go 重寫


小結

選 Go,不只是因為「大家都在用」,更是因為 Mongory 的抽象能把 Go 的長處(單檔部屬、效能、工具鏈)與既有的匹配引擎優勢結合起來。筆者相信,只要邊界拿捏正確,Go 版 Mongory 能像 Ruby 版一樣,帶給讀者「好用、可觀測、性能踏實」的體驗

Go 版)


上一篇
Day 21:跨平台預編譯與 CI +實戰案例
下一篇
Day 23:cgo 與 submodule:Makefile/腳本同步與開發體驗
系列文
Mongory:打造跨語言、高效能的萬用查詢引擎25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言