Files
arcrun-collector/cloudcheck.go
T

91 lines
3.0 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// cloudcheck.go — 每輪固定要問雲端的兩件事(版本、雲端 AI 通了沒),限一分鐘問一次
// `inkstone/arcrun-rag#121`2026-09-13 真機量測抓到的副作用)。
//
// 病:這兩發原本「每輪各打一次」。以前一輪要跑十幾秒到幾分鐘(逐檔去撞壞掉的雲端),
// 所以每小時大約幾百發。路由退避生效之後,壞掉的那條路不再被打,一輪變成五、六秒就跑完
// ⇒ **這兩發變成每 5 秒一次**。實測 geek6688 真雲端 3 分鐘:
// 退避版 80 發(其中 /health 36、空探測 36),修前只有 34 發——
// 壞掉那條路的請求變少了,雲端收到的總請求卻變多。
//
// 解:兩件事都是「狀態」,不是「工作」——一分鐘內再問一次得到的答案幾乎一定一樣。
// ⇒ 同一台知識庫一分鐘內只真的問一次,其餘沿用上一次的答案。
//
// 使用者按「立刻同步」的那一輪照問(他要的就是最新狀況)。
package collector
import (
"strings"
"sync"
"time"
)
// cloudCheckInterval=同一台知識庫多久才真的重問一次版本/雲端 AI 狀態。
var cloudCheckInterval = 60 * time.Second
type cachedVersion struct {
at time.Time
ver string
ok bool
}
type cachedAIState struct {
at time.Time
state CloudAIState
}
var (
cloudCheckMu sync.Mutex
cloudVersionSeen = map[string]cachedVersion{}
cloudAISeen = map[string]cachedAIState{}
)
// resetCloudChecks 清空快取(測試用)。
func resetCloudChecks() {
cloudCheckMu.Lock()
defer cloudCheckMu.Unlock()
cloudVersionSeen = map[string]cachedVersion{}
cloudAISeen = map[string]cachedAIState{}
}
func cloudCheckKey(cypherURL string) string {
return strings.TrimSuffix(strings.TrimSpace(cypherURL), "/")
}
// cloudVersionThrottledfetchCloudVersion,但同一台一分鐘內只真的問一次。force=立刻同步。
func cloudVersionThrottled(cypherURL string, force bool) (string, bool) {
key := cloudCheckKey(cypherURL)
now := directNow()
cloudCheckMu.Lock()
c, hit := cloudVersionSeen[key]
cloudCheckMu.Unlock()
if hit && !force && now.Sub(c.at) < cloudCheckInterval && !now.Before(c.at) {
return c.ver, c.ok
}
ver, ok := fetchCloudVersion(cypherURL)
cloudCheckMu.Lock()
cloudVersionSeen[key] = cachedVersion{at: now, ver: ver, ok: ok}
cloudCheckMu.Unlock()
return ver, ok
}
// cachedAI 回上一次(一分鐘內)問到的雲端 AI 狀態;沒有就 ok=false。
func cachedAI(cypherURL string, force bool) (CloudAIState, bool) {
if force {
return CloudAIState{}, false
}
now := directNow()
cloudCheckMu.Lock()
defer cloudCheckMu.Unlock()
c, hit := cloudAISeen[cloudCheckKey(cypherURL)]
if !hit || now.Sub(c.at) >= cloudCheckInterval || now.Before(c.at) {
return CloudAIState{}, false
}
return c.state, true
}
func rememberAI(cypherURL string, st CloudAIState) {
cloudCheckMu.Lock()
defer cloudCheckMu.Unlock()
cloudAISeen[cloudCheckKey(cypherURL)] = cachedAIState{at: directNow(), state: st}
}