fix: resolve concurrent map iteration and map write crash in sitecheck

Fixed fatal error 'concurrent map iteration and map write' that caused
nginx-ui nodes to crash and become unresponsive.

The issue occurred when the sitecheck CollectSites() method iterated over
site.IndexedSites while the cache scanner's scanForSite() was concurrently
modifying the same map. This race condition caused sporadic crashes.

Solution:
- Added GetAllIndexedSites() function in internal/site/index.go that safely
  returns a snapshot copy of the IndexedSites map while holding the read lock
- Modified CollectSites() in internal/sitecheck/checker.go to use this
  thread-safe function instead of directly accessing the global map

Fixes #1673

Co-authored-by: Jacky <me@jackyu.cn>
This commit is contained in:
Cursor Agent
2026-05-06 02:00:05 +00:00
parent 078a6764bd
commit 8d1722a1d6
2 changed files with 18 additions and 2 deletions
+13
View File
@@ -37,6 +37,19 @@ func GetIndexedSite(path string) *Index {
return &Index{}
}
// GetAllIndexedSites returns a snapshot copy of all indexed sites.
// This is safe for concurrent access as it holds the read lock while copying.
func GetAllIndexedSites() map[string]*Index {
siteIndexMutex.RLock()
defer siteIndexMutex.RUnlock()
result := make(map[string]*Index, len(IndexedSites))
for k, v := range IndexedSites {
result[k] = v
}
return result
}
func init() {
cache.RegisterCallback("site.scanForSite", scanForSite)
}
+5 -2
View File
@@ -82,11 +82,14 @@ func (sc *SiteChecker) CollectSites() {
// Clear existing sites
sc.sites = make(map[string]*SiteInfo)
// Get a thread-safe snapshot of indexed sites to avoid concurrent map access
indexedSites := site.GetAllIndexedSites()
// Debug: log indexed sites count
logger.Debugf("Found %d indexed sites", len(site.IndexedSites))
logger.Debugf("Found %d indexed sites", len(indexedSites))
// Collect URLs from indexed sites, but only from enabled sites
for siteName, indexedSite := range site.IndexedSites {
for siteName, indexedSite := range indexedSites {
// Check site status - only collect from enabled sites
siteStatus := site.GetSiteStatus(siteName)
if siteStatus != site.StatusEnabled {