88 "github.com/0xJacky/Nginx-UI/internal/nginx_log"
99 "github.com/0xJacky/Nginx-UI/internal/nginx_log/indexer"
1010 "github.com/0xJacky/Nginx-UI/model"
11+ "github.com/0xJacky/Nginx-UI/settings"
1112 "github.com/go-co-op/gocron/v2"
1213 "github.com/uozi-tech/cosy/logger"
1314)
@@ -21,19 +22,23 @@ type logIndexProvider interface {
2122func setupIncrementalIndexingJob (s gocron.Scheduler ) (gocron.Job , error ) {
2223 logger .Info ("Setting up incremental log indexing job" )
2324
24- // Run every 5 minutes to check for log file changes
25+ // Determine interval from settings, falling back to a conservative default
26+ interval := settings .NginxLogSettings .GetIncrementalIndexInterval ()
27+
28+ // Run periodically to check for log file changes using incremental indexing
2529 job , err := s .NewJob (
26- gocron .DurationJob (5 * time . Minute ),
30+ gocron .DurationJob (interval ),
2731 gocron .NewTask (performIncrementalIndexing ),
2832 gocron .WithName ("incremental_log_indexing" ),
33+ gocron .WithSingletonMode (gocron .LimitModeWait ), // Prevent overlapping executions
2934 gocron .WithStartAt (gocron .WithStartImmediately ()),
3035 )
3136
3237 if err != nil {
3338 return nil , err
3439 }
3540
36- logger .Info ("Incremental log indexing job scheduled to run every 5 minutes" )
41+ logger .Infof ("Incremental log indexing job scheduled to run every %s" , interval )
3742 return job , nil
3843}
3944
@@ -73,20 +78,41 @@ func performIncrementalIndexing() {
7378 return log .Type == "access"
7479 })
7580
81+ // Process files sequentially to avoid overwhelming the system
82+ // This is more conservative but prevents concurrent file indexing from consuming too much CPU
7683 changedCount := 0
7784 for _ , log := range allLogs {
7885 // Check if file needs incremental indexing
7986 if needsIncrementalIndexing (log , persistence ) {
80- if err := queueIncrementalIndexing (log .Path , modernIndexer , logFileManager ); err != nil {
81- logger .Errorf ("Failed to queue incremental indexing for %s: %v" , log .Path , err )
87+ logger .Infof ("Starting incremental indexing for file: %s" , log .Path )
88+
89+ // Set status to indexing
90+ if err := setFileIndexStatus (log .Path , string (indexer .IndexStatusIndexing ), logFileManager ); err != nil {
91+ logger .Errorf ("Failed to set indexing status for %s: %v" , log .Path , err )
92+ continue
93+ }
94+
95+ // Perform incremental indexing synchronously (one file at a time)
96+ if err := performSingleFileIncrementalIndexing (log .Path , modernIndexer , logFileManager ); err != nil {
97+ logger .Errorf ("Failed incremental indexing for %s: %v" , log .Path , err )
98+ // Set error status
99+ if statusErr := setFileIndexStatus (log .Path , string (indexer .IndexStatusError ), logFileManager ); statusErr != nil {
100+ logger .Errorf ("Failed to set error status for %s: %v" , log .Path , statusErr )
101+ }
82102 } else {
83103 changedCount ++
104+ // Set status to indexed
105+ if err := setFileIndexStatus (log .Path , string (indexer .IndexStatusIndexed ), logFileManager ); err != nil {
106+ logger .Errorf ("Failed to set indexed status for %s: %v" , log .Path , err )
107+ }
84108 }
85109 }
86110 }
87111
88112 if changedCount > 0 {
89- logger .Infof ("Queued %d log files for incremental indexing" , changedCount )
113+ logger .Infof ("Completed incremental indexing for %d log files" , changedCount )
114+ // Update searcher shards once after all files are processed
115+ nginx_log .UpdateSearcherShards ()
90116 } else {
91117 logger .Debug ("No log files need incremental indexing" )
92118 }
@@ -114,6 +140,23 @@ func needsIncrementalIndexing(log *nginx_log.NginxLogWithIndex, persistence logI
114140 fileModTime := fileInfo .ModTime ()
115141 fileSize := fileInfo .Size ()
116142
143+ // CRITICAL FIX: For large files (>100MB), add additional check to prevent excessive re-indexing
144+ // If the file was recently indexed (within last 30 minutes), skip it even if size increased slightly
145+ // This prevents the "infinite indexing" issue reported in #1455
146+ const largeFileThreshold = 100 * 1024 * 1024 // 100MB
147+ const recentIndexThreshold = 30 * time .Minute
148+
149+ if fileSize > largeFileThreshold && log .LastIndexed > 0 {
150+ lastIndexTime := time .Unix (log .LastIndexed , 0 )
151+ timeSinceLastIndex := time .Since (lastIndexTime )
152+
153+ if timeSinceLastIndex < recentIndexThreshold {
154+ logger .Debugf ("Skipping large file %s (%d bytes): recently indexed %v ago (threshold: %v)" ,
155+ log .Path , fileSize , timeSinceLastIndex , recentIndexThreshold )
156+ return false
157+ }
158+ }
159+
117160 if persistence != nil {
118161 if logIndex , err := persistence .GetLogIndex (log .Path ); err == nil {
119162 if logIndex .NeedsIndexing (fileModTime , fileSize ) {
@@ -157,96 +200,66 @@ func needsIncrementalIndexing(log *nginx_log.NginxLogWithIndex, persistence logI
157200 return false
158201}
159202
160- // queueIncrementalIndexing queues a file for incremental indexing
161- func queueIncrementalIndexing (logPath string , modernIndexer interface {}, logFileManager interface {}) error {
162- // Set the file status to queued
163- if err := setFileIndexStatus (logPath , string (indexer .IndexStatusQueued ), logFileManager ); err != nil {
164- return err
165- }
166-
167- // Queue the indexing job asynchronously
168- go func () {
169- defer func () {
170- // Ensure status is always updated, even on panic
171- if r := recover (); r != nil {
172- logger .Errorf ("Recovered from panic during incremental indexing for %s: %v" , logPath , r )
173- _ = setFileIndexStatus (logPath , string (indexer .IndexStatusError ), logFileManager )
174- }
175- }()
176-
177- logger .Infof ("Starting incremental indexing for file: %s" , logPath )
178-
179- // Set status to indexing
180- if err := setFileIndexStatus (logPath , string (indexer .IndexStatusIndexing ), logFileManager ); err != nil {
181- logger .Errorf ("Failed to set indexing status for %s: %v" , logPath , err )
182- return
183- }
184-
185- // Perform incremental indexing
186- startTime := time .Now ()
187- docsCountMap , minTime , maxTime , err := modernIndexer .(* indexer.ParallelIndexer ).IndexSingleFileIncrementally (logPath , nil )
188-
189- if err != nil {
190- logger .Errorf ("Failed incremental indexing for %s: %v" , logPath , err )
191- // Set error status
192- if statusErr := setFileIndexStatus (logPath , string (indexer .IndexStatusError ), logFileManager ); statusErr != nil {
193- logger .Errorf ("Failed to set error status for %s: %v" , logPath , statusErr )
194- }
195- return
196- }
197-
198- // Calculate total documents indexed
199- var totalDocsIndexed uint64
200- for _ , docCount := range docsCountMap {
201- totalDocsIndexed += docCount
203+ // performSingleFileIncrementalIndexing performs incremental indexing for a single file synchronously
204+ func performSingleFileIncrementalIndexing (logPath string , modernIndexer interface {}, logFileManager interface {}) error {
205+ defer func () {
206+ // Ensure status is always updated, even on panic
207+ if r := recover (); r != nil {
208+ logger .Errorf ("Recovered from panic during incremental indexing for %s: %v" , logPath , r )
209+ _ = setFileIndexStatus (logPath , string (indexer .IndexStatusError ), logFileManager )
202210 }
211+ }()
203212
204- // Save indexing metadata
205- duration := time .Since (startTime )
213+ // Perform incremental indexing
214+ startTime := time .Now ()
215+ docsCountMap , minTime , maxTime , err := modernIndexer .(* indexer.ParallelIndexer ).IndexSingleFileIncrementally (logPath , nil )
206216
207- if lfm , ok := logFileManager .( * indexer. LogFileManager ); ok {
208- persistence := lfm . GetPersistence ( )
209- var existingDocCount uint64
217+ if err != nil {
218+ return fmt . Errorf ( "indexing failed: %w" , err )
219+ }
210220
211- existingIndex , err := persistence .GetLogIndex (logPath )
212- if err != nil {
213- logger .Warnf ("Could not get existing log index for %s: %v" , logPath , err )
214- }
221+ // Calculate total documents indexed
222+ var totalDocsIndexed uint64
223+ for _ , docCount := range docsCountMap {
224+ totalDocsIndexed += docCount
225+ }
215226
216- // Determine if the file was rotated by checking if the current size is smaller than the last recorded size.
217- // This is a strong indicator of log rotation.
218- fileInfo , statErr := os .Stat (logPath )
219- isRotated := false
220- if statErr == nil && existingIndex != nil && fileInfo .Size () < existingIndex .LastSize {
221- isRotated = true
222- logger .Infof ("Log rotation detected for %s: new size %d is smaller than last size %d. Resetting document count." ,
223- logPath , fileInfo .Size (), existingIndex .LastSize )
224- }
227+ // Save indexing metadata
228+ duration := time .Since (startTime )
225229
226- if existingIndex != nil && ! isRotated {
227- // If it's a normal incremental update (not a rotation), we build upon the existing count.
228- existingDocCount = existingIndex .DocumentCount
229- }
230- // If the file was rotated, existingDocCount remains 0, effectively starting the count over for the new file.
230+ if lfm , ok := logFileManager .(* indexer.LogFileManager ); ok {
231+ persistence := lfm .GetPersistence ()
232+ var existingDocCount uint64
231233
232- finalDocCount := existingDocCount + totalDocsIndexed
234+ existingIndex , err := persistence .GetLogIndex (logPath )
235+ if err != nil {
236+ logger .Warnf ("Could not get existing log index for %s: %v" , logPath , err )
237+ }
233238
234- if err := lfm .SaveIndexMetadata (logPath , finalDocCount , startTime , duration , minTime , maxTime ); err != nil {
235- logger .Errorf ("Failed to save incremental index metadata for %s: %v" , logPath , err )
236- }
239+ // Determine if the file was rotated by checking if the current size is smaller than the last recorded size.
240+ // This is a strong indicator of log rotation.
241+ fileInfo , statErr := os .Stat (logPath )
242+ isRotated := false
243+ if statErr == nil && existingIndex != nil && fileInfo .Size () < existingIndex .LastSize {
244+ isRotated = true
245+ logger .Infof ("Log rotation detected for %s: new size %d is smaller than last size %d. Resetting document count." ,
246+ logPath , fileInfo .Size (), existingIndex .LastSize )
237247 }
238248
239- // Set status to indexed
240- if err := setFileIndexStatus ( logPath , string ( indexer . IndexStatusIndexed ), logFileManager ); err != nil {
241- logger . Errorf ( "Failed to set indexed status for %s: %v" , logPath , err )
249+ if existingIndex != nil && ! isRotated {
250+ // If it's a normal incremental update (not a rotation ), we build upon the existing count.
251+ existingDocCount = existingIndex . DocumentCount
242252 }
253+ // If the file was rotated, existingDocCount remains 0, effectively starting the count over for the new file.
243254
244- // Update searcher shards
245- nginx_log .UpdateSearcherShards ()
255+ finalDocCount := existingDocCount + totalDocsIndexed
246256
247- logger .Infof ("Successfully completed incremental indexing for %s, Documents: %d" , logPath , totalDocsIndexed )
248- }()
257+ if err := lfm .SaveIndexMetadata (logPath , finalDocCount , startTime , duration , minTime , maxTime ); err != nil {
258+ return fmt .Errorf ("failed to save metadata: %w" , err )
259+ }
260+ }
249261
262+ logger .Infof ("Successfully completed incremental indexing for %s, Documents: %d" , logPath , totalDocsIndexed )
250263 return nil
251264}
252265
0 commit comments