---
# System prepended metadata

title: airaRetail 要請玉清協助的地方
tags: [airaRetail]

---

---
tags: airaRetail
---

## 解決情形

## 目前的隱憂
### 1. 「檢查資料庫是否已有相似向量存在若否則新增」的這個邏輯
#### 問題描述
假設 Queue 裡面的任務有「第 0 秒來自攝影機 A 的照片」以及「第 1 秒來自攝影機 A 的照片」（capture interval  = 1000 ms）。此時 Worker 1 和 Worker 2 皆有空閑，先後取出任務，然後同時開始他們的工作。

假設這兩張照片包含的人臉特徵向量是相似的，按系統的規則，他們應該被視為相似的向量，也就是無論如何經過 Worker 1 和 Worker 2 處理後，系統最終只會保留一筆向量在資料庫中。

但由於目前沒有讀寫鎖的機制，使得（假設 Worker 1 先開始工作） Worker 1 在準備要／正在寫入時，由於 Worker 2 不知道 Worker 1 正在處理相似的向量，在資料庫也檢查不到相似向量的存在，於是最終導致 2 筆相似的向量被寫入。

#### 造成的影響
1. 同一個人在報表中會被記為兩次。
2. 向量資料庫中儲存了重複的向量，導致查詢／寫入成本上升。

#### 目前聽到／或想到的解決辦法
玉清和 Ken 提到的「讀寫鎖」。

#### 後續處理
1. 因為涉及到 inter process communication，且 process 位在不同 container（可以視為不同主機），沒有直接的 mutex / semaphore 可以用。目前是用掛 file locks 的方式，在不同的 worker 共享 lock。
2. 理論上如果能改用 RW Lock 可以再加快一些效能，但目前沒有找到現成套件可以做這件事（file、etcd 都沒有現成的 npm 套件可以用，目前只有看到 golang 上的 etcd 有），看看我們內部是不是能自己實作。


### 2. 當 capture interval 的設定值低於 1000 ms 時怎樣的 workers 的數目才不會造成 Queue 堆積又能扛起它們的工作
我們注意到當 capture interval = 1000 ms 時，所需的 workers 數目會從 8 上升至 48。在不加鎖的情況下，是可以處理完任務不讓 Queue 產生堆積。

在 workers 數為 8 時，最大 CPU Rate 大概是 1x %。
當 workers 數上升至 48 時，最大 CPU Rate 大概是 6x %。 

雖然按 CPU Rate 看來，Workers 可以再加上去。但感覺加鎖之後可能會因為彼此等待，使得再加 CPU 也無助於解決 Queue 堆積的情形。

且 Matt 說還是會有像 capture interval = 500 ms 的需求。

#### 造成的影響
1. 雖然系統能正常運作但 capture interval 無法達到業務需求所期望的目標。

#### 目前聽到／或想到的解決辦法
每個進來的向量，先按向量鄰近程度分成 N 群，Workers 只拿某一群的向量，每一群有自己的 lock，這樣可以讓鎖粒度較細，減少拿不到鎖／彼此等待的時間。

#### 後續處理
目前鎖的粒度是一個攝影機一顆鎖，理論上只要攝影機照射的範圍不重疊，同一個人臉向量就不會有競爭寫入的問題，不過同一支攝影機如果想要有 2 支以上的 worker，就會遇到讀寫時要拿鎖的問題，這會讓調升 replica 數目的效益大大降低（加再多的 workers 也沒用），應該是沒辦法很好的處理 capture interval = 1000 ms 以下的需求。

### 3. 尚未實作 Rotation 機制
因為資料庫（mongo、Qdrant）、圖床（minio）會慢慢長大。雖然儲存的資料都有按日期分開，但目前還沒正式實作 rotation 的機制。

特別是 QDrant 的 NES，如果前述重複儲存相似向量的問題無法解決，NES 裡向量數目的上升會使得系統很快就越跑越慢。

#### 目前聽到／或想到的解決辦法
1. 定期把 qdrant 裡超過一定時間、按日儲存的向量資料清除（譬如每天固定清除早於 3 個月以前的資料）。
2. 定期把 qdrant 裡超過一定時間的 NES 向量資料清除（譬如每天固定刪除早於 3 個月以前的向量）。
3. 定期把 mongo 裡超過一定時間的訪客資料 `visits`（連同會員資料 `users`）刪除。
4. 定期把 mongo 裡超過一定時間的報表資料 `dailyStats` 刪除。

#### 後續處理
無。