2022年1月25日 星期二

Vespa 的新功能 hash dictionary

在翻閱 Vespa 的部落格 [1] 時,看到在 2021 年 5 月時,Vespa 新增了 hash dictionary 的功能,所以就來紀錄一下這個功能的細節。

hash dictionary 是什麼?

在 Vespa 的設計中,當欄位被設定為 attribute 時,可以另外加上 fast-search 的設定,讓 Vespa 自動幫這個欄位建立 index 以加快搜尋速度。原本 Vespa 的 fast-search 只能夠使用 b-tree 的資料結構來建立,但現在我們可以選擇使用 hash table 的資料結構來建立 index,在特殊的情境下能夠獲得比 b-tree 更好一點的效能。

hash dictionary 的限制

目前測試發現 hash dictionary 需要設定為 cased,而且必須要同時在 dictionarymatch 兩個設定上都加上 cased 才能通過檢查。不過這點我覺得在文件 [2] 上並沒有很明確地點出…。

field id type string {
    indexing: summary | attribute
    attribute: fast-search
    dictionary {
        hash
        cased
    }
    match: cased
}
hash dictionary 的效果

要比較效果的話,首先需要先看一下它的比較對象,也就是預設的 btree dictionary。對工程師來說,看到 b-tree 跟 hash 兩個關鍵字,應該大概就知道差別是什麼了!簡要來說就是 O(logn) 跟 O(1) 的差別 XD。不過除此之外,由於上述的 hash dictionary 的限制,在 Vespa 上設定 hash dictionary 還會另外衍生出 case-sensitive 的議題需要考慮。

首先先看一下 btree 的狀況,如果使用以下的設定的話,btree 的預設行為是 uncased,意味著 "bear" = "BEAR" = "Bear"

field id type string {
    indexing: summary | attribute
    attribute: fast-search
}

實際使用 [3] 建立出來的測試環境來測試的話,裡面有一個 asin: "B00GQ22Y6Y" 的 document,內容長這樣:

{
    "pathId": "/document/v1/item/item/docid/B00GQ22Y6Y",
    "id": "id:item:item::B00GQ22Y6Y",
    "fields": {
        "title": "Trendy Style Hand-knit Warm Lining inside Winter Bucket Hat w. Cute Flower-Purple #H01",
        "asin": "B00GQ22Y6Y",
        ...(skipped)...
    }
}

此時用以下兩個 YQL 都能夠查到這個 document。這主要是因為預設的設定是 uncased,因此不管大小寫都可以順利查到結果。

SELECT * FROM item WHERE asin contains "b00gq22y6y";
SELECT * FROM item WHERE asin contains "B00GQ22Y6Y";

不過由於使用 hash dictionary 時,會需要設定 cased 屬性,導致更換成以下的 hash dictionary 時,狀況就會不太一樣了:

field id type string {
    indexing: summary | attribute
    attribute: fast-search
    dictionary {
        hash
        cased
    }
    match: cased
}

這時其實結果是 asin contains "b00gq22y6y" 可以查到資料,但 asin contains "B00GQ22Y6Y" 反而查不到…。這結果其實蠻出乎我的意料,不知道是不是 bug 或者是使用方式不正確之類的。

參考資料
  1. Vespa Product Updates, May 2021
  2. Schema Reference – dictionary
  3. 準備 Vespa 測試環境

Gradle 的 ‘plugin’ 區塊限制

很緩慢地直到去年年底才開始摸 Gradle,然後最近要試著自己弄小專案時,撞到一個奇怪的問題。我用 Gradle init 的指令幫忙產生第一版的 Gradle 設定,接著在根目錄的 settings.gradle 想要加入 java plugin,例如下面這樣:

plugins {
    id "java"
}

repositories {
    mavenCentral()
}

rootProject.name = 'sample-vespa-data-feeder'
include('app')

結果意外地(我很意外 XD…)遇到了以下的錯誤訊息:

An exception occurred applying plugin request [id: 'java']
> Failed to apply plugin 'org.gradle.java'.
   > Could not create plugin of type 'JavaPlugin'.
      > Unable to determine constructor argument #3: missing parameter of type JvmPluginServices, or no service of type JvmPluginServices.

結果到處亂看的時候,看到 [1] 才赫然發現,原來 plugin 是新的用法,而且這個用法好像不能寫在 root project 上。後來我把 plugins {}repositories {} 都改放進 app/build.gradle 就正常了….。

BTW,小小的題外話,我用 Gradle init 產生設定時,是選擇 single project 的,不過它產生出來的還是具有 multi project 的結構,總覺得 Gradle 的設計是不是其實根本沒有 single project….?

參考資料
  1. What the difference in applying gradle plugin

2022年1月4日 星期二

[書籤] 阿里技术专家详解DDD系列

是說中國的文章最讓人困擾的地方,就是轉錄文章都不用記載的,所以超難分辨到底哪個才是原作者發的…。這裡單純只留我覺得比較容易閱讀的連結。不過第五篇是直接連接到可能是源出處的地方,因為轉錄的網站好像還沒收錄到它。(但我覺得轉錄的網站把排版排得比較易讀 XD)

  1. Domain Primitive
  2. 第二弹 - 应用架构
  3. 第三讲 - Repository模式
  4. 第四讲:领域层设计规范
  5. 第五讲:聊聊如何避免写流水账代码