# 生命週期

追蹤 daemon 從啟動到關閉的過程，以及期間有哪些 goroutine 並行執行。

## 啟動順序

`runServer` 以 `--daemon` 在新 session 中啟動目前執行檔，將 stdout 與 stderr 導向 `daemon.log`，並每 100 ms 輪詢一次 `endpoint` 檔案，最多 10 秒。`runServerDaemon` 接著依下列順序初始化：

1. 以新 UID、PID 與啟動時間寫入 `runtime.uid`。失敗（包含已有另一個存活的 daemon）只記錄警告，啟動繼續。
2. 初始化 service `kuradb` 的 keychain context。
3. 建立 OpenAI embedder；缺少憑證時程序結束。
4. 開啟 `global.db`、預載符合預期維度的 query embedding，並註冊將新 query embedding 持久化的 hook。
5. 初始化 gse tokenizer 與 process-wide vector cache。
6. 從 `db.json` 載入 database registry；讀取錯誤時程序結束。
7. 對每個已註冊資料庫：確保目錄與 `~/Kura_{db}` 符號連結存在、開啟 `data.db`、建立 vector bucket，並從已 embedding 且未 dismiss 的 row 重建 bucket。
8. 為每個已載入資料庫啟動一個 embedding scheduler 與一個 watcher。
9. 在 `127.0.0.1` 啟動 HTTP server、寫入 `endpoint`，並等待 `SIGINT` 或 `SIGTERM`。

`data.db` 開啟前的單一資料庫失敗會略過該資料庫。Vector bucket 建立失敗時，該資料庫仍可做關鍵字查詢，但不會啟動 watcher 與 embedder。

## 並行模型

| 工作 | Goroutine | 協調方式 |
|---|---|---|
| Watcher | 每個已載入資料庫一個，10 秒 ticker | 寫入 `record.json` 的 snapshot 在 goroutine 中執行，以 mutex 保護 |
| Embedding scheduler | 每個已載入資料庫一個，5 秒 ticker | 每批最多 64 個 pending chunk |
| 搜尋 | 未指定 `target` 時，keyword 與 semantic 分支並行執行 | `search.Search` 內的 `sync.WaitGroup`，所有 transport 共用 |
| Query cache 持久化 | 每個新 query embedding 一個短命 goroutine | 每次寫入 `global.db` 逾時 5 秒 |
| HTTP server | 一個 listener goroutine 與一個 shutdown 監看者 | Context 取消 |

SQLite 透過 `go-sqlkit` 提供八條 read connection 與獨立 write connection。Vector cache 使用 cache 層級與每個 bucket 各自的 `RWMutex`；query cache 有自己的 `RWMutex`。

## 關閉流程

終止 signal 會取消共用 context，各部分各自反應：

| 部分 | 動作 |
|---|---|
| HTTP server | 移除 `endpoint`，再以 5 秒逾時呼叫 `Shutdown` |
| Watcher 與 embedder | 離開 select loop |
| 主 goroutine | 清除 `runtime.uid`、關閉所有 per-database connector，最後關閉 `global.db` |

主 goroutine 不等待 HTTP shutdown 或各 loop 結束，因此程序可能在進行中的請求完成前就退出。`kura stop` 會送出 `SIGTERM`，5 秒後程序仍存活則升級為 `SIGKILL`。
