From 68c21db4547c5abd9f4440e1a052b46fe05e72bd Mon Sep 17 00:00:00 2001 From: Johannes Rest Date: Sun, 18 Oct 2026 11:43:20 +0200 Subject: [PATCH] Pause optional ai-rem update jobs during backup snapshots --- README.md | 4 ++++ UPDATE_ANLEITUNG.md | 16 ++++++++++++++++ tests/test_backup.py | 10 +++++++++- update.sh | 11 ++++++++++- 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b4bcde5..7059829 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,10 @@ lokale Docker-Image vor und prüft Initialisierung, Import und Bereinigung. Nach Backup mit Remote-Dateiprüfung aktiviert es den Monatstimer. `--enable-ai-rem` nimmt auf Gemini `/srv/ai-rem` und `/etc/ai-rem` samt Dienststopps wieder auf; siehe [Update-Anleitung](UPDATE_ANLEITUNG.md). +Ein installierter `ai-rem-managed-update.timer` und sein Dienst werden dabei +ebenfalls vor dem Snapshot angehalten und anschließend fortgesetzt. Die +[ai-rem-Betriebsautomatisierung](https://gitea.jr-it-services.de/johannes/ai-rem-maintenance) +nutzt zusätzlich die konfigurierte Backup-Sperre, um parallele Updates zu vermeiden. [Speicherbereinigung und Fehlerbehebung](SPEICHER.md) beschreibt die automatische Entfernung erkannter Arbeitsreste und die geschützte lokale Notfallaufbewahrung. Ohne Aktivierungsoption bleibt ein bisher deaktivierter Restore-Test deaktiviert. diff --git a/UPDATE_ANLEITUNG.md b/UPDATE_ANLEITUNG.md index e32e5df..5cd6161 100644 --- a/UPDATE_ANLEITUNG.md +++ b/UPDATE_ANLEITUNG.md @@ -73,6 +73,22 @@ Marker, begrenzte Wiederholungen, Signalabbrüche und Mount-/Recovery-Schutz. Paketinstallation, ai-rem-Einbindung und Rollback bestanden ebenfalls. Das ersetzt nicht die noch ausstehende Installation und Speicher-/Backup-Abnahme auf Gemini. +## Ergänzung vom 18.10.2026: ai-rem-Updates beim Snapshot berücksichtigen + +`--enable-ai-rem` berücksichtigt jetzt auch `ai-rem-managed-update.timer` und +`ai-rem-managed-update.service`, sofern diese auf dem Host installiert sind. +Beide werden vor den bisherigen ai-rem-Diensten gestoppt und nach dem +Anwendungsstart wieder aufgenommen. Alte Hosts ohne Update-Timer bleiben +unterstützt; wiederholte Installation erzeugt keine doppelten Stopps. + +Die eigene [ai-rem-Betriebsautomatisierung](https://gitea.jr-it-services.de/johannes/ai-rem-maintenance) +richtet die täglichen Updates und einen persistenten Modell-Cache ein, verwendet +die konfigurierte `LOCKFILE` und ergänzt die Update-Units in der bestehenden +Backup-Konfiguration. Die app-backup-Ergänzung ist zusätzlich für spätere +Paketinstallationen notwendig, damit `--enable-ai-rem` die Stoppreihenfolge erhält. +Die erfolgreiche Produktionsinstallation des ai-rem-Updaters ist am 18.10.2026 +noch nicht bestätigt; der zuletzt geprüfte Dienst läuft auf 1.3.0 ohne Update-Timer. + ## Update vom 16.09.2026: Upload ohne Archiv-Rücklesen, ai-rem wieder aufnehmen Der tägliche Lauf lädt Archive und Manifest über Crypt hoch und prüft anschließend diff --git a/tests/test_backup.py b/tests/test_backup.py index a9a877d..494b363 100644 --- a/tests/test_backup.py +++ b/tests/test_backup.py @@ -821,7 +821,13 @@ else: raise SystemExit('Unexpected Docker command') f.write(f'EXTRA_PATHS=("{old_extra}")\n') package=self.build_package() self.env['TEST_METADATA_ONLY']='true' - for attempt in range(2): + for attempt in range(3): + if attempt == 1: + optional=['ai-rem-managed-update.timer','ai-rem-managed-update.service'] + state=json.loads(self.state.read_text()) + for unit in optional: state['services'][unit]='active' + self.state.write_text(json.dumps(state)) + units=optional+units self.events.write_text('') result=subprocess.run(['bash',str(package),'--foreground','--enable-ai-rem'], env=self.env,capture_output=True,text=True,timeout=120) @@ -834,6 +840,8 @@ else: raise SystemExit('Unexpected Docker command') self.assertEqual([(a['label'],a['source']) for a in extra], [('extra-0',str(old_extra)),('extra-1','/srv/ai-rem'),('extra-2','/etc/ai-rem')]) events=self.events.read_text() + if attempt == 0: + self.assertNotIn('systemctl stop ai-rem-managed-update.',events) for unit in units: self.assertEqual(events.count('systemctl stop '+unit+' '),1) self.assertLess(events.index('systemctl stop '+unit+' '),events.index('mariadb-dump')) diff --git a/update.sh b/update.sh index 9c030af..4ff4e9a 100755 --- a/update.sh +++ b/update.sh @@ -225,13 +225,22 @@ if [[ $UP_WORKER == false ]]; then remaining=() for unit in "${QUIESCE_SERVICES[@]}"; do case ${unit%.service} in - ai-rem|ai-rem-healthcheck|ai-rem-backup|ai-rem-prune|ai-rem-healthcheck.timer|ai-rem-backup.timer|ai-rem-prune.timer) ;; + ai-rem|ai-rem-healthcheck|ai-rem-backup|ai-rem-prune|ai-rem-managed-update|ai-rem-healthcheck.timer|ai-rem-backup.timer|ai-rem-prune.timer|ai-rem-managed-update.timer) ;; *) remaining+=("$unit") ;; esac done for unit in "${ai_rem_units[@]}"; do [[ $(systemctl show "$unit" --property=LoadState --value) == loaded ]] || die "Missing ai-rem unit: $unit" done + # Older hosts have no managed updater yet. When installed, stop its timer + # and job before taking the database snapshot; resume them after ai-rem. + optional_ai_rem_units=() + for unit in ai-rem-managed-update.timer ai-rem-managed-update.service; do + if [[ $(systemctl show "$unit" --property=LoadState --value) == loaded ]]; then + optional_ai_rem_units+=("$unit") + fi + done + ai_rem_units=("${optional_ai_rem_units[@]}" "${ai_rem_units[@]}") QUIESCE_SERVICES=("${ai_rem_units[@]}" "${remaining[@]}") echo 'ai-rem included: /srv/ai-rem and /etc/ai-rem; container and maintenance jobs stopped during snapshot' fi