fix(server): thread request ctx through session bootstrap, drop Background DB calls

- getOrCreateSession now takes ctx; GetSessionOwnerID/
  RestoreSessionContext/CompilePromptGuidance run under
  context.WithTimeout(5s) instead of context.Background()
- SyncRecentDialogue's rolling summary uses a 30s timeout context
  instead of Background, so client disconnect cancels the LLM call

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
weichen
2026-09-19 05:03:01 +00:00
co-authored by Cursor
parent e2b13406db
commit a81db16cdd
4 changed files with 19 additions and 5 deletions
+3 -1
View File
@@ -176,7 +176,9 @@ func (s *Session) SyncRecentDialogue() {
// 若发生了截断且存在早期消息,同步计算或加载滚动摘要
if truncated && len(early) > 0 && s.CtxMgr != nil {
s.syncDialogueSummary(context.Background(), early)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
s.syncDialogueSummary(ctx, early)
}
}
+2 -1
View File
@@ -143,7 +143,7 @@ func (s *Server) handleGetProject(w http.ResponseWriter, r *http.Request) {
return
}
sess, err := s.getOrCreateSession(0, sessionID, "", "", "", "", "", "", "", "")
sess, err := s.getOrCreateSession(r.Context(), 0, sessionID, "", "", "", "", "", "", "", "")
if err != nil || sess == nil || sess.UserCtx == nil {
writeSafeError(w, r, http.StatusNotFound, "session or project context not found")
return
@@ -170,6 +170,7 @@ func (s *Server) handleChat(w http.ResponseWriter, r *http.Request) {
req.WorkDir = ""
sess, err := s.getOrCreateSession(
r.Context(),
user.ID,
req.SessionID,
req.ProjectName,
+13 -3
View File
@@ -403,6 +403,7 @@ func (s *Server) cleanStaleSessionsLocked(ttl time.Duration) int {
// getOrCreateSession 获取或动态构建会话。callerUserID>0 时校验持久层归属,不匹配返回 ErrSessionOwnerMismatch。
func (s *Server) getOrCreateSession(
ctx context.Context,
callerUserID int64,
sessionID string,
projectName string,
@@ -420,7 +421,10 @@ func (s *Server) getOrCreateSession(
// 归属校验必须先于任何缓存命中/持久层灌入
if callerUserID > 0 && s.ctxMgr != nil {
if owner, err := s.ctxMgr.GetSessionOwnerID(context.Background(), sessionID); err == nil && owner > 0 && owner != callerUserID {
dbCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
owner, err := s.ctxMgr.GetSessionOwnerID(dbCtx, sessionID)
cancel()
if err == nil && owner > 0 && owner != callerUserID {
return nil, ctxmgr.ErrSessionOwnerMismatch
}
}
@@ -455,7 +459,10 @@ func (s *Server) getOrCreateSession(
// 尝试从持久层恢复会话上下文 (服务重启后跨进程保持 G3)
var restored *ctxmgr.RestoredSessionContext
if s.ctxMgr != nil {
if r, err := s.ctxMgr.RestoreSessionContext(context.Background(), sessionID); err == nil && r != nil && len(r.Messages) > 0 {
dbCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
r, err := s.ctxMgr.RestoreSessionContext(dbCtx, sessionID)
cancel()
if err == nil && r != nil && len(r.Messages) > 0 {
restored = r
}
}
@@ -563,7 +570,10 @@ func (s *Server) getOrCreateSession(
}
if sess.UserID > 0 && sess.CtxMgr != nil {
if guidance, err := sess.CtxMgr.CompilePromptGuidance(context.Background(), sess.UserID); err == nil && guidance != "" {
dbCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
guidance, err := sess.CtxMgr.CompilePromptGuidance(dbCtx, sess.UserID)
cancel()
if err == nil && guidance != "" {
_ = coordinator.ApplyContextUpdate(userCtx, coordinator.ContextUpdate{
ExpectedVersion: userCtx.Version,
SetPersonalizedGuidance: true,
+1
View File
@@ -174,6 +174,7 @@ func (s *Server) handleChatStream(w http.ResponseWriter, r *http.Request) {
// 会话与项目绑定必须在 SSE headers/Flush 之前完成授权(与 tasks/run/stream 对齐)
sess, err := s.getOrCreateSession(
r.Context(),
user.ID,
req.SessionID,
req.ProjectName,