Files
goclaw/pkg/browser/browser_reaper.go
T
viettranx ee4d34ae28 feat(browser): add timeout, idle auto-close, and max-pages safety mechanisms
Prevent resource leaks and hanging actions in the browser tool:
- Per-action context timeout (default 30s, configurable via timeoutMs param or config)
- Idle page reaper goroutine closes pages unused for 10min (configurable)
- Max pages per tenant (default 5) with LRU eviction
- RefStore cleanup on page close/evict/reap to prevent memory leaks
2026-03-25 13:55:33 +07:00

55 lines
1.2 KiB
Go

package browser
import "time"
// runReaper periodically closes pages that have been idle longer than idleTimeout.
// Runs as a goroutine; exits when stopReaper is closed.
func (m *Manager) runReaper() {
ticker := time.NewTicker(60 * time.Second)
defer ticker.Stop()
for {
select {
case <-m.stopReaper:
return
case <-ticker.C:
m.reapIdlePages()
}
}
}
// reapIdlePages closes pages idle longer than idleTimeout.
func (m *Manager) reapIdlePages() {
m.mu.Lock()
defer m.mu.Unlock()
if m.browser == nil {
return
}
now := time.Now()
for targetID, lastUsed := range m.pageLastUsed {
if now.Sub(lastUsed) <= m.idleTimeout {
continue
}
page, ok := m.pages[targetID]
if !ok {
delete(m.pageLastUsed, targetID)
continue
}
if err := page.Close(); err != nil {
m.logger.Warn("reaper: failed to close idle page", "targetId", targetID, "error", err)
continue
}
delete(m.pages, targetID)
delete(m.console, targetID)
delete(m.pageTenants, targetID)
delete(m.pageLastUsed, targetID)
m.refs.Remove(targetID)
m.logger.Info("reaper: closed idle page", "targetId", targetID, "idle", now.Sub(lastUsed).Round(time.Second))
}
}