Der asynchrone Settings-Writer hatte — anders als der Session-Writer — keinen
Generations-Schutz. Lief eine periodische async Settings-Speicherung gerade,
während gleichzeitig synchron saveSettings() lief (z.B. Nutzer ändert eine
Option, oder ein Settings-Backup wird wiederhergestellt), konnte der async
rename die frisch synchron geschriebene Datei wieder mit dem ALTEN Stand
überschreiben. Die gerade gespeicherte Änderung war damit auf der Platte (und im
.bak) verloren — bis zur nächsten Speicherung.
Fix: Der Settings-Pfad spiegelt jetzt exakt den bereits abgesicherten
Session-Pfad:
- Eigener Generations-Zähler syncSettingsSaveGeneration; saveSettings() (sync)
erhöht ihn.
- writeSettingsPayload bekommt die zum Zeitpunkt des Aufrufs erfasste Generation
und bricht vor rename UND vor dem EXDEV-Copy ab, wenn inzwischen eine
synchrone Speicherung passiert ist (generation < aktuell).
- saveSettingsAsync/saveSettingsPayloadAsync trägt die ORIGINAL-Generation auch
durch die Warteschlange (vorher wurde beim Abarbeiten der Queue eine frische
Generation erfasst → hätte den Schutz ausgehebelt).
- Eigener Zähler statt Wiederverwendung von syncSaveGeneration, damit eine
synchrone Settings-Speicherung keine laufenden async SESSION-Schreibvorgänge
fälschlich verwirft (keine Datei-übergreifende Kopplung).
Zusätzlich (verwandt): shutdown() ruft jetzt cancelPendingAsyncSaves() vor der
finalen synchronen Speicherung auf, damit eine noch laufende/eingereihte async
Settings-Schreibung den Shutdown-Save nicht mehr überholen kann.
Test: in-flight + eingereihte async Settings-Speicherung (ALT), dann synchron
NEU — nach dem Settle steht NEU auf der Platte. Ohne den Generations-Schutz
gewinnt ALT (rot bewiesen).