// results.go:任务结果缓存 + /result 查询支撑。 // // 契约(A3.1):GET /result/{request_id}:done → Envelope;queued/running → // 202 + {status};无 → 404。done 信封写进程内 bounded LRU(50 条,与 gateway // 缓存同量级);schema 禁改(W1 纪律)→ 信封不另立列,进程内缓存为权威读面, // 缓存淘汰后由 gateway 按 202 轮询引导重查。 package scheduler import ( "encoding/json" "sync" "time" "onesvm.com/onesvm/browser-server/internal/contract" ) // resultEntry 结果条目。 type resultEntry struct { env *contract.Envelope setAt time.Time } // resultCache 进程内 done 信封缓存(bounded LRU)。 type resultCache struct { mu sync.Mutex cap int items map[string]*resultEntry order []string // LRU 序(头部最旧) } func newResultCache() *resultCache { return &resultCache{cap: 50, items: map[string]*resultEntry{}} } // put 写入(超 cap 淘汰最旧)。 func (c *resultCache) put(reqID string, env *contract.Envelope) { if reqID == "" || env == nil { return } c.mu.Lock() defer c.mu.Unlock() if _, exists := c.items[reqID]; !exists { c.order = append(c.order, reqID) } c.items[reqID] = &resultEntry{env: env, setAt: time.Now()} for len(c.order) > c.cap { oldest := c.order[0] c.order = c.order[1:] delete(c.items, oldest) } } // get 读(命中提升 LRU 位)。 func (c *resultCache) get(reqID string) (*contract.Envelope, bool) { c.mu.Lock() defer c.mu.Unlock() e, ok := c.items[reqID] if !ok { return nil, false } return e.env, true } // buildResultEnvelope 适配器执行结果 → Envelope(模版层 Build 的调用包装)。 func buildResultEnvelope(t *Template, in TemplateInput) *contract.Envelope { return t.Build(TemplateInput{ Job: in.Job, Adapter: in.Adapter, ProxyExit: in.ProxyExit, Cached: in.Cached, Raw: in.Raw, Warnings: in.Warnings, TookMs: in.TookMs, }) } // jsonRaw 信封序列化(/result 内嵌用)。 func jsonRaw(e *contract.Envelope) json.RawMessage { b, err := json.Marshal(e) if err != nil { return json.RawMessage(`{"ok":false,"error":{"code":"upstream","message":"信封序列化失败"}}`) } return b }