feat(wecom-aibot): enhance stream task management with StreamClosedAt and improved cleanup logic
This commit is contained in:
parent
4e09c91dda
commit
a87e6b0551
1 changed files with 38 additions and 18 deletions
|
|
@ -46,6 +46,7 @@ type streamTask struct {
|
||||||
CreatedTime time.Time
|
CreatedTime time.Time
|
||||||
Deadline time.Time // ~30s, we close the stream here and switch to response_url
|
Deadline time.Time // ~30s, we close the stream here and switch to response_url
|
||||||
StreamClosed bool // stream returned finish:true; waiting for agent to reply via response_url
|
StreamClosed bool // stream returned finish:true; waiting for agent to reply via response_url
|
||||||
|
StreamClosedAt time.Time // set when StreamClosed becomes true; used for accelerated cleanup
|
||||||
Finished bool // fully done
|
Finished bool // fully done
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
answerCh chan string // receives agent reply from Send()
|
answerCh chan string // receives agent reply from Send()
|
||||||
|
|
@ -781,6 +782,7 @@ func (c *WeComAIBotChannel) getStreamResponse(task *streamTask, timestamp, nonce
|
||||||
// Only mark stream as closed; keep in chatTasks for Send() to find.
|
// Only mark stream as closed; keep in chatTasks for Send() to find.
|
||||||
task.mu.Lock()
|
task.mu.Lock()
|
||||||
task.StreamClosed = true
|
task.StreamClosed = true
|
||||||
|
task.StreamClosedAt = time.Now()
|
||||||
task.mu.Unlock()
|
task.mu.Unlock()
|
||||||
// Remove from streamTasks (no more stream polls expected).
|
// Remove from streamTasks (no more stream polls expected).
|
||||||
c.taskMu.Lock()
|
c.taskMu.Lock()
|
||||||
|
|
@ -1117,13 +1119,24 @@ func (c *WeComAIBotChannel) cleanupLoop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanupOldTasks removes tasks that have been alive longer than 1 hour
|
// cleanupOldTasks removes tasks that have exceeded their expected lifetime:
|
||||||
// (response_url validity window), which is the absolute maximum lifetime of any task.
|
// - Active tasks (in streamTasks): cleaned up after 1 hour (response_url validity window).
|
||||||
|
// - StreamClosed tasks (in chatTasks only): cleaned up after streamClosedGracePeriod.
|
||||||
|
// These tasks are waiting for the agent to call Send() via response_url. If the agent
|
||||||
|
// crashes or times out without calling Send(), we must not let them accumulate indefinitely.
|
||||||
|
// The grace period is generous enough to cover typical LLM latency but far shorter than 1 hour,
|
||||||
|
// preventing chatTasks from filling up when many requests time out in quick succession.
|
||||||
|
const (
|
||||||
|
streamClosedGracePeriod = 10 * time.Minute // max wait for agent after stream closes
|
||||||
|
taskMaxLifetime = 1 * time.Hour // absolute max (≈ response_url validity)
|
||||||
|
)
|
||||||
|
|
||||||
func (c *WeComAIBotChannel) cleanupOldTasks() {
|
func (c *WeComAIBotChannel) cleanupOldTasks() {
|
||||||
c.taskMu.Lock()
|
c.taskMu.Lock()
|
||||||
defer c.taskMu.Unlock()
|
defer c.taskMu.Unlock()
|
||||||
|
|
||||||
cutoff := time.Now().Add(-1 * time.Hour)
|
now := time.Now()
|
||||||
|
cutoff := now.Add(-taskMaxLifetime)
|
||||||
for id, task := range c.streamTasks {
|
for id, task := range c.streamTasks {
|
||||||
if task.CreatedTime.Before(cutoff) {
|
if task.CreatedTime.Before(cutoff) {
|
||||||
delete(c.streamTasks, id)
|
delete(c.streamTasks, id)
|
||||||
|
|
@ -1143,12 +1156,19 @@ func (c *WeComAIBotChannel) cleanupOldTasks() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Also clean up StreamClosed tasks from chatTasks that are older than 1 hour.
|
// Clean up StreamClosed tasks from chatTasks.
|
||||||
// These were removed from streamTasks earlier but kept alive for response_url delivery.
|
// Two expiry conditions are checked:
|
||||||
|
// 1. Absolute expiry: task was created more than taskMaxLifetime ago.
|
||||||
|
// 2. Grace expiry: stream closed more than streamClosedGracePeriod ago
|
||||||
|
// (agent had enough time to reply; it is not coming back).
|
||||||
for chatID, queue := range c.chatTasks {
|
for chatID, queue := range c.chatTasks {
|
||||||
filtered := queue[:0]
|
filtered := queue[:0]
|
||||||
for _, t := range queue {
|
for _, t := range queue {
|
||||||
if !t.Finished && t.CreatedTime.After(cutoff) {
|
absoluteExpired := t.CreatedTime.Before(cutoff)
|
||||||
|
graceExpired := t.StreamClosed &&
|
||||||
|
!t.StreamClosedAt.IsZero() &&
|
||||||
|
t.StreamClosedAt.Before(now.Add(-streamClosedGracePeriod))
|
||||||
|
if !t.Finished && !absoluteExpired && !graceExpired {
|
||||||
filtered = append(filtered, t)
|
filtered = append(filtered, t)
|
||||||
} else if !t.Finished {
|
} else if !t.Finished {
|
||||||
t.cancel() // cancel any lingering agent goroutine
|
t.cancel() // cancel any lingering agent goroutine
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue