fix: resolve concurrent map iteration and map write crash in sitecheck (#1661)

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: Cursor Agent <cursoragent@cursor.com>
This commit is contained in:
Jacky
2026-05-06 11:28:57 +08:00
committed by GitHub
parent 078a6764bd
commit ba2bbe3ac2
2 changed files with 18 additions and 2 deletions

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)
}

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 {