Modulkontexte an die maßgebliche Datenbank; Shell-Shutdown weiter untersucht
Zwei Befunde aus dem ersten Dauerbetrieb auf dem Testserver. #487: Der Fix zu #389 hatte nur den Core auf DatabaseEndpoint umgestellt. Alle vier Modulkontexte lasen die Konfiguration weiter selbst und landeten auf dem Platzhalter aus EffectiveConnectionString, sobald die Datenbank nur in settings.json steht - "Access denied for user 'root'@'localhost'", waehrend der Core im selben Prozess sauber arbeitete. Gefunden 30 Sekunden nach dem ersten Start des Dienstes; kein Test und kein --check hatte es gesehen, weil --check nur die Core-Verbindung oeffnet. Die Kontexte loesen DatabaseEndpoint jetzt ueber die IServiceProvider-Ueberladung von AddDbContextFactory auf. Der Platzhalter meldet sich ausserdem nicht mehr als root an, sondern als "KEINE-DATENBANK-KONFIGURIERT" - die alte Meldung sah nach einem Rechteproblem aus, obwohl gar keine Verbindung konfiguriert war. #396 traegt NICHT. Die Pruefung auf der VM zeigt: Das Signal kommt an, der Handler laeuft (Logzeile steht), aber der Prozess bleibt stehen. Zwei Ursachen sind dabei aufgedeckt und behoben worden, eine dritte ist offen: 1. Ueber Avalonia.Application.Current war die Lebensdauer aus dem Signal-Thread nicht erreichbar - der Handler lief ins Leere. Sie wird jetzt im Hauptthread erzeugt (SetupWithLifetime + lifetime.Start) und dem Handler mitgegeben. 2. Die Sicherheitsabfrage der Shell fing das Schliessen ab und wartete auf einen Klick, den bei einem Signal niemand macht. Ein Signal ist keine Frage; der Handler uebergeht die Abfrage jetzt ausdruecklich. 3. OFFEN: Der Dispatcher-Rueckruf wird nicht ausgefuehrt - weder die Erfolgs- noch die Fehlerzeile erscheint, obwohl beide protokolliert wuerden. Der UI-Thread nimmt die Nachricht offenbar nicht an. Die Messpunkte bleiben im Code: ohne sie war von aussen nicht zu unterscheiden, ob das Signal ankommt, ob die Lebensdauer fehlt oder ob der Rueckruf haengt. 404 Tests gruen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -43,16 +43,29 @@ internal static class Program
|
||||
{
|
||||
App.Services = host.Services;
|
||||
|
||||
// Die Lebensdauer wird hier selbst erzeugt statt über StartWithClassicDesktopLifetime.
|
||||
// Grund: Der Signal-Handler unten braucht sie, und über Avalonia.Application.Current
|
||||
// ist sie aus einem fremden Thread nicht verlässlich zu bekommen. Am 2026-09-05 auf
|
||||
// der Test-VM gemessen: Das SIGTERM kam an, aber der Handler fand keine Lebensdauer
|
||||
// und lief ins Leere - die Shell blieb stehen wie vor dem Fix. Mit der Referenz aus
|
||||
// dem Hauptthread ist der Zugriff eindeutig.
|
||||
var lifetime = new ClassicDesktopStyleApplicationLifetime
|
||||
{
|
||||
Args = args,
|
||||
ShutdownMode = Avalonia.Controls.ShutdownMode.OnLastWindowClose
|
||||
};
|
||||
|
||||
// SIGTERM/SIGINT geordnet behandeln. Die Avalonia-Nachrichtenschleife endet sonst nur,
|
||||
// wenn das letzte Fenster geschlossen wird: beim Abmelden, beim Herunterfahren des
|
||||
// Rechners und bei jedem systemctl stop bekam die Shell ein SIGTERM, ignorierte es und
|
||||
// wurde nach Ablauf der Frist mit SIGKILL abgeräumt – mitten im Worker-Lauf, ohne
|
||||
// Modul- und Host-Stopp. Der Daemon behandelt beide Signale über den Host bereits.
|
||||
using var sigterm = RegisterShutdownSignal(PosixSignal.SIGTERM, logger);
|
||||
using var sigint = RegisterShutdownSignal(PosixSignal.SIGINT, logger);
|
||||
var uiHost = host.Services.GetRequiredService<Shell.AvaloniaUiHost>();
|
||||
using var sigterm = RegisterShutdownSignal(PosixSignal.SIGTERM, logger, lifetime, uiHost);
|
||||
using var sigint = RegisterShutdownSignal(PosixSignal.SIGINT, logger, lifetime, uiHost);
|
||||
|
||||
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
||||
return 0;
|
||||
BuildAvaloniaApp().SetupWithLifetime(lifetime);
|
||||
return lifetime.Start(args);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -67,18 +80,38 @@ internal static class Program
|
||||
/// Host-Stopp greift. Steht die Oberfläche noch nicht, bleibt die Vorgabebehandlung des
|
||||
/// Signals stehen; kennt die Plattform das Signal nicht, läuft die Shell wie bisher weiter.
|
||||
/// </summary>
|
||||
private static IDisposable? RegisterShutdownSignal(PosixSignal signal, LoggingService logger)
|
||||
private static IDisposable? RegisterShutdownSignal(
|
||||
PosixSignal signal, LoggingService logger, IClassicDesktopStyleApplicationLifetime lifetime,
|
||||
Shell.AvaloniaUiHost uiHost)
|
||||
{
|
||||
try
|
||||
{
|
||||
return PosixSignalRegistration.Create(signal, ctx =>
|
||||
{
|
||||
if (Avalonia.Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
|
||||
return;
|
||||
|
||||
ctx.Cancel = true; // sonst endet der Prozess sofort – ohne Modul- und Host-Stopp
|
||||
// Die Vorgabebehandlung würde den Prozess beenden, ohne den finally-Zweig mit
|
||||
// Modul- und Host-Stopp zu erreichen - und sie tut es bei einer laufenden
|
||||
// Avalonia-Schleife ohnehin nicht zuverlässig (am 2026-09-05 nachgestellt: der
|
||||
// Prozess blieb einfach stehen). Deshalb übernehmen wir das Signal ganz.
|
||||
ctx.Cancel = true;
|
||||
logger.Info("Core", $"{signal} empfangen – Oberfläche wird geordnet beendet.");
|
||||
Dispatcher.UIThread.Post(() => desktop.Shutdown());
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
// Fehler in diesem Rückruf verschwinden sonst spurlos - er läuft auf dem
|
||||
// UI-Thread, und niemand wartet auf sein Ergebnis.
|
||||
try
|
||||
{
|
||||
// Ohne diesen Schritt fängt die Sicherheitsabfrage der Shell das Schließen
|
||||
// ab und wartet auf einen Klick, den bei einem Signal niemand macht.
|
||||
uiHost.SkipShutdownConfirmation();
|
||||
logger.Info("Core", "Sicherheitsabfrage übergangen, beende die Oberfläche.");
|
||||
lifetime.Shutdown();
|
||||
logger.Info("Core", "Shutdown angefordert.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error("Core", $"Herunterfahren nach {signal} gescheitert: {ex.Message}", ex);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
Reference in New Issue
Block a user