209 lines
8.0 KiB
Markdown
209 lines
8.0 KiB
Markdown
# darktable-sync
|
||
|
||
Bidirektionale Synchronisation der Darktable-Datenbank und Fotobibliothek zwischen
|
||
einem lokalen Linux-Rechner und einem Server (Synology NAS) via SSH.
|
||
|
||
## Aktueller Stand
|
||
|
||
Die Skripte synchronisieren DB und Fotos mit **Unison** über SSH. Der Wrapper startet
|
||
einen Pre-Sync vor und einen Post-Sync nach jedem Darktable-Start.
|
||
|
||
```
|
||
darktable_wrapper.sh → Pre-Sync → Darktable starten → Post-Sync
|
||
darktable_sync.sh → Unison-Sync (DB + Fotos, bidirektional)
|
||
darktable_common.sh → Gemeinsame Hilfsfunktionen
|
||
```
|
||
|
||
Voraussetzungen: Unison (gleiche Version auf Client und Server), SSH-Key-Auth.
|
||
|
||
---
|
||
|
||
## Architektur-Entscheidungsprotokoll
|
||
|
||
Dieses Dokument hält fest, welche Sync-Ansätze untersucht wurden und warum bestimmte
|
||
Entscheidungen getroffen oder offengelassen wurden.
|
||
|
||
---
|
||
|
||
### Problem 1: Upload-Delete-Safety (rsync --delete)
|
||
|
||
Das ursprüngliche System verwendete `rsync --delete` für den Foto-Upload. Das führt
|
||
zu einem strukturellen Problem:
|
||
|
||
**rsync kennt keinen Unterschied zwischen:**
|
||
- Datei lokal gelöscht → soll auch auf Server gelöscht werden ✓
|
||
- Datei lokal nie vorhanden (weil ein anderer Client sie hochgeladen hat) → darf NICHT gelöscht werden ✗
|
||
|
||
**Konkretes Beispiel:** `2025-08_Nordkap_Handy/` existiert nur auf dem Server (von
|
||
Smartphone hochgeladen), nicht auf dem Laptop. Beim nächsten Upload löscht rsync den
|
||
gesamten Ordner vom Server — dauerhafter Datenverlust.
|
||
|
||
**Erster Lösungsansatz:** rsync-Manifest — eine Textdatei mit allen zuletzt
|
||
synchronisierten Pfaden. Beim Upload: fehlt lokal + im Manifest → gelöscht → OK.
|
||
Fehlt lokal + nicht im Manifest → nie synchronisiert → nicht löschen.
|
||
|
||
**Verworfen**, weil Unison dasselbe Problem architektonisch sauber löst: Unison pflegt
|
||
intern Archive über den letzten Sync-Zustand und leitet daraus korrekt ab, ob eine
|
||
fehlende Datei gelöscht oder nie vorhanden war.
|
||
|
||
---
|
||
|
||
### Analyse der Sync-Alternativen
|
||
|
||
#### Unison (aktuell implementiert)
|
||
|
||
**Vorteile:**
|
||
- Internes Archiv löst Upload-Delete-Safety nativ
|
||
- Triggered Sync (kein Daemon) — passt zur Pre/Post-Sync-Architektur
|
||
- Transport via SSH, kein zusätzlicher Dienst auf dem Server nötig
|
||
- Bidirektionaler Sync in einem Aufruf pro Replica-Paar
|
||
|
||
**Konfliktverhalten:**
|
||
- DB-Konflikte: bestehender Sync-Token-Dialog bleibt erhalten → User entscheidet
|
||
(`-force local` / `-force remote`)
|
||
- Foto-Konflikte: `-prefer newer` (neuere Datei gewinnt, kein Dialog)
|
||
|
||
**Kritischer Nachteil: Synology-Installation**
|
||
Unison erfordert exakt die gleiche Version auf Client und Server — und nicht nur die
|
||
Unison-Version, sondern auch die zugrundeliegende OCaml-Compiler-Version. Auf einer
|
||
Synology NAS gibt es kein `apt`. Alle bekannten Anleitungen für Unison auf Synology
|
||
(Stand 2025) stammen aus 2011–2021 und beschreiben aufwändige Cross-Compilation, die
|
||
bei jedem DSM-Update erneut erforderlich wird. Entware könnte helfen, aber die
|
||
Versionskompatibilität ist nicht garantiert. **Unison auf Synology ist ein dauerhaftes
|
||
Wartungsproblem.**
|
||
|
||
---
|
||
|
||
#### Syncthing
|
||
|
||
**Vorteile:**
|
||
- SynoCommunity bietet ein aktiv gepflegtes Paket für DSM 7.x (v2.0.14, Stand 2025)
|
||
— einfache Installation über Package Center
|
||
- Kein Versionszwang zwischen Nodes
|
||
- Upload-Delete-Safety: Syncthing weiß aus seiner eigenen Datenbank, was es
|
||
synchronisiert hat, und löscht nie Dateien anderer Devices
|
||
- Transparente Konfliktbehandlung via `.sync-conflict-...`-Dateien
|
||
- Eingebaute Versionierung (File Versioning)
|
||
|
||
**Nachteil: Always-On-Daemon**
|
||
Syncthing läuft kontinuierlich. Das kollidiert mit der triggered-sync-Architektur
|
||
(Pre/Post-Sync um Darktable herum). Das Pausieren via CLI ist technisch möglich
|
||
(`syncthing cli config folders <id> paused set true/false`), macht die Wrapper-Logik
|
||
aber komplexer.
|
||
|
||
**Das SQLite-Problem:**
|
||
Darktable verwendet SQLite im WAL-Modus. Dabei entstehen drei Dateien:
|
||
`library.db`, `library.db-wal`, `library.db-shm`. Syncthing synct auf Dateiebene
|
||
ohne SQLite-Semantik zu verstehen. Bei aktivem Sync während Darktable läuft könnte
|
||
eine inkonsistente DB-Kombination auf der anderen Seite ankommen. Ausserdem erzeugt
|
||
SQLite-Checkpointing mtime-Updates ohne inhaltliche Änderung — das kann zu
|
||
Falsch-Konflikten führen.
|
||
|
||
**Lösung für das WAL-Problem:** `.db-wal` und `.db-shm` via `.stignore` ausschließen.
|
||
Dann synct Syncthing nur `library.db`, die nach Darktable-Beendigung immer konsistent
|
||
ist (SQLite checkpointed beim Schließen).
|
||
|
||
**Das eigentliche Konflikt-Problem:**
|
||
Wenn Darktable auf zwei Rechnern unabhängig voneinander läuft und beide eine divergierte
|
||
`library.db` erzeugen, entsteht ein echter inhaltlicher Konflikt — den kein Sync-Tool
|
||
automatisch auflösen kann (SQLite-Merging wäre dazu nötig). Syncthing macht diesen
|
||
Konflikt mit einer `.sync-conflict`-Datei sichtbar, was ehrlicher ist als so zu tun,
|
||
als könnte man ihn "lösen".
|
||
|
||
---
|
||
|
||
#### Gegenüberstellung
|
||
|
||
| Kriterium | rsync (original) | Unison (aktuell) | Syncthing |
|
||
|---|---|---|---|
|
||
| Upload-Delete-Safety | ✗ (Problem) | ✓ (Archiv) | ✓ (Device-DB) |
|
||
| Synology-Installation | einfach | sehr schwierig | einfach (SynoCommunity) |
|
||
| DB-Konflikt-Erkennung | Token + Dialog | Token + Dialog | .sync-conflict-Datei |
|
||
| DB-Konflikt-Auflösung | User entscheidet | User entscheidet | automatisch (neuere Datei) |
|
||
| Kein Daemon nötig | ✓ | ✓ | ✗ |
|
||
| Triggered Sync | ✓ | ✓ | nur mit Pause/Resume |
|
||
| Foto-Versioning | ✗ | Backup-Dir | ✓ eingebaut |
|
||
|
||
---
|
||
|
||
### Offene Architekturentscheidung
|
||
|
||
Es stehen zwei grundlegend verschiedene Ansätze zur Wahl:
|
||
|
||
#### Option A: Script-System beibehalten (Unison)
|
||
|
||
Das bestehende System mit `darktable_sync.sh` bleibt, mit Unison als Sync-Engine.
|
||
Voraussetzung: Unison muss auf der Synology NAS installierbar sein (Entware oder
|
||
manuelles Binary). Der Workflow (Pre/Post-Sync, Token-Dialog, Active-Marker,
|
||
Versions-Check) bleibt erhalten.
|
||
|
||
**Offen:** Ist Unison auf der DS234+ mit einer zum Client kompatiblen Version
|
||
installierbar? Das muss praktisch getestet werden.
|
||
|
||
#### Option B: Vollständig auf Syncthing umstellen
|
||
|
||
Das gesamte Script-System entfällt. Syncthing übernimmt die Synchronisation
|
||
dauerhaft. Der `darktable_wrapper.sh` entfällt oder wird auf Pause/Resume-Steuerung
|
||
reduziert. Das Repo würde zu einem Setup-Guide mit `.stignore`-Template schrumpfen.
|
||
|
||
**Vorteile:** Kein eigener Code zu pflegen, bewährte Software, einfache
|
||
Synology-Installation.
|
||
|
||
**Nachteile:** Kein expliziter Versions-Check (Darktable-Versionen müssen manuell
|
||
übereinstimmen), kein Active-Marker (kein Schutz gegen gleichzeitige Nutzung auf
|
||
mehreren Rechnern), Konflikte werden automatisch statt durch User-Entscheidung
|
||
aufgelöst.
|
||
|
||
---
|
||
|
||
### Empfohlene nächste Schritte
|
||
|
||
1. Testen: Ist Unison via Entware auf der DS234+ mit kompatibler Version installierbar?
|
||
- Falls ja → Option A ist machbar
|
||
- Falls nein → Option B (Syncthing) ist der pragmatische Weg
|
||
|
||
2. Bei Option B:
|
||
- Syncthing auf DS234+ via SynoCommunity installieren
|
||
- `.stignore` für `*.db-wal`, `*.db-shm` konfigurieren
|
||
- File Versioning in Syncthing aktivieren
|
||
- Unison-Migration (aktueller Branch) reverten
|
||
|
||
---
|
||
|
||
## Installation (aktueller Stand: Unison)
|
||
|
||
Voraussetzung: Unison muss auf Client **und** Server installiert sein, exakt gleiche
|
||
Version.
|
||
|
||
```bash
|
||
# Client (Ubuntu/Debian)
|
||
sudo apt install unison
|
||
|
||
# Server (Synology) — noch ungeklärt, siehe Architektur-Entscheidungsprotokoll oben
|
||
|
||
git clone https://gitea.troeger-net.org/martin/darktable-sync.git
|
||
cd darktable-sync
|
||
cp .env.example ~/.config/darktable-sync/.env
|
||
chmod 600 ~/.config/darktable-sync/.env
|
||
nano ~/.config/darktable-sync/.env
|
||
./install.sh
|
||
```
|
||
|
||
## Konfiguration
|
||
|
||
Datei: `~/.config/darktable-sync/.env` (Permissions: 600)
|
||
|
||
| Variable | Bedeutung |
|
||
|---|---|
|
||
| `SERVER_IP` | IP oder Hostname des Servers |
|
||
| `SERVER_USER` | SSH-Benutzer auf dem Server |
|
||
| `SERVER_SSH_PORT` | SSH-Port (Standard: 22) |
|
||
| `SERVER_DB_DIR` | Pfad zur Darktable-DB auf dem Server |
|
||
| `SERVER_PHOTO_DIR` | Pfad zur Fotobibliothek auf dem Server |
|
||
| `LOCAL_DARKTABLE_DB_DIR` | Lokaler Darktable-DB-Pfad |
|
||
| `LOCAL_PHOTO_DIR` | Lokaler Fotobibliothek-Pfad |
|
||
|
||
## Lizenz
|
||
|
||
MIT
|