@@ -198,15 +198,24 @@ def schedule_all_repositories():
198
198
except Exception as e :
199
199
logger .error (f"Error scheduling repositories on startup: { e } " )
200
200
201
- # Flag to ensure we only initialize once
201
+ # Thread-safe flag to ensure we only initialize once
202
+ import threading
203
+ _scheduler_lock = threading .Lock ()
202
204
_scheduler_initialized = False
203
205
204
206
def ensure_scheduler_initialized ():
205
- """Ensure scheduler is initialized with existing repositories"""
207
+ """Ensure scheduler is initialized with existing repositories (thread-safe) """
206
208
global _scheduler_initialized
207
- if not _scheduler_initialized :
208
- schedule_all_repositories ()
209
- _scheduler_initialized = True
209
+ if _scheduler_initialized :
210
+ return
211
+
212
+ with _scheduler_lock :
213
+ # Double-check pattern to avoid race conditions
214
+ if not _scheduler_initialized :
215
+ logger .info ("Initializing scheduler with existing repositories..." )
216
+ schedule_all_repositories ()
217
+ _scheduler_initialized = True
218
+ logger .info ("Scheduler initialization completed" )
210
219
211
220
@login_manager .user_loader
212
221
def load_user (user_id ):
@@ -215,7 +224,6 @@ def load_user(user_id):
215
224
@app .route ('/' )
216
225
@login_required
217
226
def dashboard ():
218
- ensure_scheduler_initialized ()
219
227
repositories = Repository .query .filter_by (user_id = current_user .id ).all ()
220
228
recent_jobs = BackupJob .query .filter_by (user_id = current_user .id ).order_by (BackupJob .created_at .desc ()).limit (10 ).all ()
221
229
return render_template ('dashboard.html' , repositories = repositories , recent_jobs = recent_jobs )
@@ -647,14 +655,15 @@ def backup_with_context():
647
655
).first ()
648
656
649
657
if running_job :
650
- logger .warning (f"Backup already running for repository { repo .name } , skipping" )
658
+ logger .warning (f"Backup already running for repository { repo .name } (job { running_job . id } ) , skipping" )
651
659
return
652
660
653
- # Additional check: ensure no backup started in the last 5 minutes to prevent rapid duplicates
661
+ # Additional check: ensure no backup started in the last 30 seconds to prevent rapid duplicates
662
+ recent_cutoff = datetime .utcnow () - timedelta (seconds = 30 )
654
663
recent_backup = BackupJob .query .filter_by (
655
664
repository_id = repository .id
656
665
).filter (
657
- BackupJob .started_at > datetime . utcnow () - timedelta ( minutes = 5 )
666
+ BackupJob .started_at > recent_cutoff
658
667
).first ()
659
668
660
669
if recent_backup :
@@ -754,5 +763,15 @@ def backup_with_context():
754
763
else :
755
764
logger .error (f"Failed to schedule job { job_id } " )
756
765
766
+ # Initialize scheduler with existing repositories at startup
767
+ # This runs after all functions are defined
768
+ try :
769
+ with app .app_context ():
770
+ logger .info ("Starting scheduler initialization at app startup..." )
771
+ ensure_scheduler_initialized ()
772
+ logger .info ("Scheduler initialization at startup completed" )
773
+ except Exception as e :
774
+ logger .error (f"Failed to initialize scheduler at startup: { e } " )
775
+
757
776
if __name__ == '__main__' :
758
777
app .run (host = '0.0.0.0' , port = 8080 , debug = False )
0 commit comments