Browse Source

feat: logs intermédiaires et diagnostic timeout amélioré

Persiste le log et l'archive_name en DB dès que la sauvegarde locale est
terminée, avant le transfert. Ainsi, si le cleanup 6h se déclenche pendant
un transfert long, le log indique déjà que l'archive est créée et où en
était le transfert.

Le message de timeout distingue maintenant deux cas :
- archive locale présente → timeout pendant le transfert
- pas d'archive → backup lui-même trop long

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cedric Hansen 1 month ago
parent
commit
0f7f1012b1
2 changed files with 37 additions and 12 deletions
  1. 23 10
      sources/jobs/ynh_backup.py
  2. 14 2
      sources/scheduler.py

+ 23 - 10
sources/jobs/ynh_backup.py

@@ -3,7 +3,7 @@ import os
 import subprocess
 from datetime import datetime
 
-from db import db, Job, Run
+from db import db, Job, Run, _size_human
 
 
 BACKUP_DIR = None  # initialisé depuis app.config
@@ -41,42 +41,55 @@ def execute_job(job_id):
         from jobs.utils import sudo_getsize
         size_bytes = sudo_getsize(archive_path) or None
 
-        run.status = "success"
+        # Checkpoint 1 : archive créée — persister immédiatement
         run.archive_name = archive_name
         run.size_bytes = size_bytes
-        run.log_text = log
+        run.log_text = f"[archive] {archive_name} créée ({_size_human(size_bytes)})\n\n{log}"
+        db.session.commit()
 
         from retention import apply_retention
         deleted = apply_retention(job, archive_name, backup_dir)
         if deleted:
-            run.log_text += f"\n\nRétention : {len(deleted)} archive(s) supprimée(s) : {', '.join(deleted)}"
+            run.log_text += f"\n\nRétention locale : {len(deleted)} archive(s) supprimée(s) : {', '.join(deleted)}"
+            db.session.commit()
 
-        # Transfert automatique post-backup
+        # Checkpoint 2 : transfert
         if job.destination_id:
             from db import Destination
             dest = db.session.get(Destination, job.destination_id)
             if dest and dest.enabled:
                 data_dir = current_app.config["DATA_DIR"]
+                run.log_text += f"\n\nTransfert → {dest.remote_str} : démarré…"
+                db.session.commit()
                 try:
                     from jobs.transfer import transfer_archive
                     transfer_log = transfer_archive(archive_name, dest, backup_dir, data_dir)
-                    run.log_text += f"\n\nTransfert → {dest.remote_str} :\n{transfer_log}"
+                    run.log_text += f"\n{transfer_log}"
                 except Exception as transfer_exc:
-                    run.log_text += f"\n\n⚠ Transfert échoué vers {dest.remote_str} :\n{transfer_exc}"
+                    run.log_text += f"\n⚠ Transfert échoué : {transfer_exc}"
+                db.session.commit()
         elif job.remote_instance_id:
             from db import RemoteInstance
             inst = db.session.get(RemoteInstance, job.remote_instance_id)
             if inst:
+                run.log_text += f"\n\nTransfert HTTP → {inst.name} ({inst.url}) : démarré…"
+                db.session.commit()
                 try:
                     from jobs.transfer import push_archive_to_instance
                     transfer_log = push_archive_to_instance(archive_name, inst, backup_dir, job=job)
-                    run.log_text += f"\n\nTransfert HTTP → {inst.name} :\n{transfer_log}"
+                    run.log_text += f"\n{transfer_log}"
                 except Exception as transfer_exc:
-                    run.log_text += f"\n\n⚠ Transfert HTTP échoué vers {inst.name} :\n{transfer_exc}"
+                    run.log_text += f"\n⚠ Transfert HTTP échoué : {transfer_exc}"
+                db.session.commit()
+
+        run.status = "success"
 
     except Exception as exc:
         run.status = "error"
-        run.log_text = str(exc)
+        if run.log_text:
+            run.log_text += f"\n\nErreur fatale : {exc}"
+        else:
+            run.log_text = str(exc)
 
     finally:
         run.finished_at = datetime.utcnow()

+ 14 - 2
sources/scheduler.py

@@ -52,10 +52,22 @@ def _cleanup_stuck_runs():
             hours = int(duration.total_seconds() // 3600)
             minutes = int((duration.total_seconds() % 3600) // 60)
             run.status = "error"
+            run.finished_at = now
+
+            if run.archive_name:
+                # L'archive a été créée localement ; le timeout est survenu pendant le transfert
+                detail = (
+                    f" Archive locale présente ({run.archive_name})."
+                    " Le processus de transfert était probablement encore en cours."
+                )
+            else:
+                # Le backup lui-même n'a pas terminé dans les 6h
+                detail = " Aucune archive locale détectée : la création du backup n'a pas abouti dans les délais."
+
             run.log_text = (run.log_text or "") + (
-                f"\n[timeout] Run bloqué depuis {hours}h{minutes:02d} — marqué en erreur par le nettoyage automatique."
+                f"\n\n[timeout] Run bloqué depuis {hours}h{minutes:02d} — "
+                f"marqué en erreur par le nettoyage automatique.{detail}"
             )
-            run.finished_at = now
         if stuck:
             db.session.commit()