v0.18.10:讓「同步引擎沒在跑」說出死因(leo Windows 實測①②的診斷前置)

leo 08-06 Windows 回報:「一直在『看守中』和『沒有在跑』中間閃,要我重啟但重啟無效,
我覺得它在跑個迴圈不停重複」+「加一個資料夾明顯沒產生看守資料夾」。
朋友的一般 PC(非 ARM)也一樣,停在等待中 ⇒ **不是 ARM 模擬的問題,是 Windows 通用**。

## 已定位的機制(不是根因,是「為什麼查不出根因」)
閃爍=子行程一啟動就死 → supervisor 退避重拉 → 狀態在 Starting(alive=true)與
Error(false)之間彈跳 ⇒ 畫面跟著閃。而**死因一直被記在 Status().LastError 裡,
從來沒有上過畫面、也沒寫進 app.log** ⇒ 使用者只看到閃爍與「請重新開啟」,
重開當然無效(死因沒變)。這是「安靜地略過」的同一種病換地方發作。

## 這版做的
- 死因上畫面:重試 >= 3 次改顯示「同步引擎一直啟動失敗,已自動重試 N 次|原因:…」,
  且**黏住不閃**(不受重起過程中的 Starting 影響)
- 每次異常結束寫進 app.log(同錯誤 10 次內只記一次,避免洗爆)

## 排除掉的嫌疑(查過,不是)
- PDF 引擎(pdfium/wasm):**延遲初始化**,第一次讀 PDF 才啟動 ⇒ 不會在啟動時炸
- ARM 模擬:leo 朋友的一般 x64 PC 同樣症狀
- 待查嫌疑:Wails 的 Windows 版是 `-H windowsgui`(GUI subsystem,無主控台),
  而 v0.18.8 的 collector.exe 是 `go build` 的 console subsystem——
  v0.18.9 合併成單一 binary 後,子行程換成 GUI subsystem 的自己。**尚未證實。**

## 順帶修好自己的閘(它擋對了我)
指紋閘擋下「v0.18.10 已對應另一份原始碼」——因為第一次打包失敗、版號卻已被戳。
⇒ 加判準:**磁碟上沒有該版號產物=從未出貨,允許重戳**(不用去手改 JSON,
那種爛示範遲早被改成「都放行」)。

## 安裝程式(leo ③)尚未兌現,誠實記錄
`wails build -nsis` 需要 makensis;Homebrew 的 3.12 在這台 macOS **連兩行的最小腳本
都在寫檔階段丟 std::bad_alloc**,zlib/bzip2/lzma 三壓縮器全崩,brew extract 舊版
被 homebrew/core 擋。且帶 -nsis 會讓 wails 整個中止、連 exe 都不產
⇒ build-win.sh 改成**先煙霧測試 makensis**,壞的就只出裸 exe 並大聲警告。
This commit is contained in:
2026-08-06 18:27:08 +08:00
parent 0b8bc83bd1
commit 6698f9fbda
6 changed files with 128 additions and 26 deletions
+41
View File
@@ -64,6 +64,23 @@ func startSupervisor() {
}
sup = supervisor.New(bin, cfg)
sup.ArgPrefix = collectorArgPrefix() // 同一支執行檔,換身分跑
// 🔴 2026-08-06:每次狀態變成 Error 都寫進 app.log。
// 沒有這一段,「子行程一啟動就死」在檔案裡**一個字都沒有**——
// leo 在 Windows ARM 撞到時,畫面只有閃爍、log 只有「同步引擎已啟動」,
// 死因(Status().LastError)只活在記憶體裡,跟著程式一起消失。
// 只在錯誤訊息**變了**或每 10 次才記一次,避免重試迴圈把 log 洗爆。
var lastLogged string
var lastN int
sup.SetOnChange(func(st supervisor.Status) {
if st.State != supervisor.StateError {
return
}
if st.LastError == lastLogged && st.Restarts-lastN < 10 {
return
}
lastLogged, lastN = st.LastError, st.Restarts
appLog("同步引擎異常結束(第 %d 次重試):%s", st.Restarts, st.LastError)
})
sup.Start()
appLog("同步引擎已啟動:%s", bin)
}
@@ -146,6 +163,30 @@ func installSignalHandler() {
// 這不是第三種判斷法——db17f28(08-05)已為「同步中」立下同一條路:
// 「改讀 sup.Status().State ⇒ **接回既有機制,不發明第三種判斷法**」。
// 當時只改了 collectorSyncing 那半邊,這裡是把漏掉的另一半補上。
//
// collectorFailure 回報「同步引擎是不是一直啟動失敗」,以及死因。
//
// 🔴 2026-08-06 leo 在 Windows 11 ARM 實測:畫面「一直在『看守中』和『沒有在跑』
//
// 中間閃…我覺得它在跑個迴圈不停重複」。
// 真相就是那樣——子行程一啟動就死,supervisor 退避後再拉,狀態在
// StartingcollectorAlive=true)與 Errorfalse)之間彈跳 ⇒ 畫面跟著閃。
// **而死因一直被記在 Status().LastError 裡,從來沒有上過畫面**
// ⇒ 使用者只看到閃爍與「請重新開啟」,重開當然無效(死因沒變)。
// 這是「安靜地略過」的同一種病,換一個地方發作。
//
// restarts 達到門檻才算「一直失敗」——偶爾重起一次是正常的(例如換設定)。
func collectorFailure() (msg string, restarts int, looping bool) {
if sup == nil {
return "", 0, false
}
st := sup.Status()
return st.LastError, st.Restarts, st.Restarts >= crashLoopThreshold
}
// crashLoopThreshold=重起幾次算「一直失敗」。3 次以內可能只是換設定或暫時性錯誤。
const crashLoopThreshold = 3
func collectorAlive() bool {
if sup == nil {
return false