package fileproc import ( "runtime" "sync/atomic" "time" "github.com/sirupsen/logrus" "github.com/ivuorinen/gibidify/gibidiutils" ) // ValidateFileProcessing checks if a file can be processed based on resource limits. func (rm *ResourceMonitor) ValidateFileProcessing(filePath string, fileSize int64) error { if !rm.enabled { return nil } rm.mu.RLock() defer rm.mu.RUnlock() // Check if emergency stop is active if rm.emergencyStopRequested { return gibidiutils.NewStructuredError( gibidiutils.ErrorTypeValidation, gibidiutils.CodeResourceLimitMemory, "processing stopped due to emergency memory condition", filePath, map[string]interface{}{ "emergency_stop_active": true, }, ) } // Check file count limit currentFiles := atomic.LoadInt64(&rm.filesProcessed) if int(currentFiles) >= rm.maxFiles { return gibidiutils.NewStructuredError( gibidiutils.ErrorTypeValidation, gibidiutils.CodeResourceLimitFiles, "maximum file count limit exceeded", filePath, map[string]interface{}{ "current_files": currentFiles, "max_files": rm.maxFiles, }, ) } // Check total size limit currentTotalSize := atomic.LoadInt64(&rm.totalSizeProcessed) if currentTotalSize+fileSize > rm.maxTotalSize { return gibidiutils.NewStructuredError( gibidiutils.ErrorTypeValidation, gibidiutils.CodeResourceLimitTotalSize, "maximum total size limit would be exceeded", filePath, map[string]interface{}{ "current_total_size": currentTotalSize, "file_size": fileSize, "max_total_size": rm.maxTotalSize, }, ) } // Check overall timeout if time.Since(rm.startTime) > rm.overallTimeout { return gibidiutils.NewStructuredError( gibidiutils.ErrorTypeValidation, gibidiutils.CodeResourceLimitTimeout, "overall processing timeout exceeded", filePath, map[string]interface{}{ "processing_duration": time.Since(rm.startTime), "overall_timeout": rm.overallTimeout, }, ) } return nil } // CheckHardMemoryLimit checks if hard memory limit is exceeded and takes action. func (rm *ResourceMonitor) CheckHardMemoryLimit() error { if !rm.enabled || rm.hardMemoryLimitMB <= 0 { return nil } var m runtime.MemStats runtime.ReadMemStats(&m) currentMemory := gibidiutils.SafeUint64ToInt64WithDefault(m.Alloc, 0) if currentMemory > rm.hardMemoryLimitBytes { rm.mu.Lock() defer rm.mu.Unlock() // Log violation if not already logged violationKey := "hard_memory_limit" if !rm.violationLogged[violationKey] { logrus.Errorf("Hard memory limit exceeded: %dMB > %dMB", currentMemory/1024/1024, rm.hardMemoryLimitMB) rm.violationLogged[violationKey] = true } if rm.enableGracefulDegr { // Force garbage collection runtime.GC() // Check again after GC runtime.ReadMemStats(&m) currentMemory = gibidiutils.SafeUint64ToInt64WithDefault(m.Alloc, 0) if currentMemory > rm.hardMemoryLimitBytes { // Still over limit, activate emergency stop rm.emergencyStopRequested = true return gibidiutils.NewStructuredError( gibidiutils.ErrorTypeValidation, gibidiutils.CodeResourceLimitMemory, "hard memory limit exceeded, emergency stop activated", "", map[string]interface{}{ "current_memory_mb": currentMemory / 1024 / 1024, "limit_mb": rm.hardMemoryLimitMB, "emergency_stop": true, }, ) } // Memory freed by GC, continue with degradation rm.degradationActive = true logrus.Info("Memory freed by garbage collection, continuing with degradation mode") } else { // No graceful degradation, hard stop return gibidiutils.NewStructuredError( gibidiutils.ErrorTypeValidation, gibidiutils.CodeResourceLimitMemory, "hard memory limit exceeded", "", map[string]interface{}{ "current_memory_mb": currentMemory / 1024 / 1024, "limit_mb": rm.hardMemoryLimitMB, }, ) } } return nil }