Erstellen eines Datenbankdumps


  • Mod

    @uvbey sagte in Erstellen eines Datenbankdumps:

    wie bekommst Du das eigentlich so schön rot markiert?

    Das Forum benutzt Markdown-Syntax. Für inline-Codestücke macht man zwei einzelne Anführungszeichen drumrum: `mein code' wird dann zu mein code (hier habe ich natürlich getrickst beim Vorführen, damit er das erste "mein code" nicht rot macht). Viele Formatierungen (aber nicht diese), sind auch über die Schaltflächen über dem Textfenster verfügbar.

    Hier eine Übersicht über gängige Formatierungen:
    https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet
    Das Forum macht davon nicht alles (Bilder und Videos sind bewusst deaktiviert), dafür ein paar Sachen mehr (z.B.Mathematik über Dollarzeichen: i=0n1qi=1qn1q\sum_{i=0}^{n-1}q^i=\frac{1-q^n}{1-q}).


  • Gesperrt

    @SeppJ sagte in Erstellen eines Datenbankdumps:

    (Bilder und Videos sind bewusst deaktiviert)

    Aus Sicherheitsgründen?


  • Mod

    Nein.



  • @SeppJ

    Danke, das funktioniert!!



  • @uvbey sagte in Erstellen eines Datenbankdumps:

    Da stehe ich auch gerade auf dem Schlauch

    ..jetzt wird das zumindest fehlerfrei compiliert - ich habe dabei nur myProcess_Exited vorgezogen:

    
    void myProcess_Exited(System::Object^ sender, System::EventArgs^ e)
    {
    	std::remove("mysqldump.sql");							// sql-dump löschen
    }
    void backup_database(void)
    {
            ---
            Process^ myProcess = gcnew Process();
            myProcess->StartInfo->UseShellExecute = false;
            myProcess->StartInfo->CreateNoWindow = true;
            myProcess->EnableRaisingEvents = true;
            myProcess->Exited += gcnew EventHandler(myProcess_Exited);
            myProcess->Start("mysqldump", mysqldump_arguments);						// sql-dump erstellen
            return;
    }
    
    

    Das Programm läuft fehlerfrei, allerdings wird der generierte sql-dump nicht gelöscht, d.h. myProcess_Exitedwird gar nicht aufgerufen. Irgendwelche Ideen?

    Aus meinen Anfängen der C-Programmierung weiß ich noch, dass Funktionsaufrufe stets vor der aufrufenden Funktion zu platzieren sind. Doch zwischenzeitlich ist das m.W. zumindest normalerweise nicht mehr der Fall. Warum spielt das jetzt eine Rolle?



  • Vermutlich beendet sich dein Programm bevor der Dump fertig ist.



  • @DocShoe

    ...das glaube ich weniger, da auch ein Sleep(5000) vor dem return nichts gebracht hat...



  • @uvbey

    Dann musst du uns wohl doch mal relevante Abschnitte aus deinem Quelltext zeigen.

    • Wann wird backup_database() aufgerufen?
    • Wie stellst du sicher, dass dein Programm länger läuft als das Datenbankbackup? Wie sind überhaupt die Bedingungen für dein Programmende?


  • Das ist mein Hauptprogramm, welches über Form1 die eigentliche Funktionalität aufruft. Wenn dies beendet wird (durch Schließen von Form1 mittels x ), wird backup_database() aufgerufen:

    using namespace System;
    using namespace System::Diagnostics;
    using namespace System::Windows::Forms;
    using namespace System::IO;
    
    [STAThread]
    int main()
    {
    
    	::ShowWindow(::GetConsoleWindow(), SW_HIDE);
    
    	Application::EnableVisualStyles();
    	Application::SetCompatibleTextRenderingDefault(false);
    	Application::Run(gcnew CppCLRWinFormsProject::Form1());
    
    	dump_db::backup_database();
    
    	return(0);
    }
    

    Nach dessen Rücksprung wird das Programm beendet.
    Das Datenbankbackup ist aktuell 270 kB groß - mit Daten von über einem Jahr. In 20 Jahren erreichen wir also etwa 5 MB, was immer noch sehr schnell abgearbeitet werden sollte (und dann sollte mich das nicht mehr interessieren ;-).
    Solange ich keine bessere Lösung habe, habe ich sleep auf 500ms gestellt.

    BTW: Wann ist eigentlich ein Prozess beendet? Wenn beispielsweise der Aufruf von mysqldump beendet ist, oder wenn mysqldump beendet ist?? Ich habe mit einem Beispiel von Microsoft in C# gespielt, in dem über Process.Starteine kleine Applikation gestartet wird. Sobald die Applikation aufgerufen ist, wird der Prozess beendet. Wenn das auch bei mysqldump so ist, bringt mir die ganze Warterei auf das Prozessende überhaupt nichts...



  • Wenn ein Prozess gestartet und danach aber die Hauptfunktion main sofort beendet wird, so wird diese Anwendung sofort beendet (und kann nicht mehr auf ein Ereignis warten). Du mußt sicherstellen, daß noch aktiv gewartet wird. Schau mal in Übersicht über Synchronisierungsprimitive - ich würde dir hierfür AutoResetEvent bzw. ManualResetEvent (s. die Beispiele dort bzgl. WaitOne() und Set()) empfehlen.

    Trotzdem sollte eigentlich WaitForExit() hier funktionieren (und wäre auch der empfohlene Weg).



  • Und da haben wir schon den Fehler gefunden 😉

    Windows-Anwendungen sind nachrichtengesteuert, alles passiert über Window-Messages und Message-Queues. Dazu braucht es aber eine Instanz, die diese Message-Queues behandelt und die darin enthaltenen Messages an die jeweiligen Empfänger weiterleitet. In deinem Fall wird das von der Funktion Application::Run erledigt. Dein Backup erstellst du aber, nachdem die Run Funktion verlassen wurde, d.h. alle eventuell jetzt noch aufgelaufenen Windows-Messages werden nicht mehr behandelt. Damit wird auch das Process::Exited Ereignis nicht mehr ausgelöst.

    BTW: Wann ist eigentlich ein Prozess beendet? Wenn beispielsweise der Aufruf von mysqldump beendet ist, oder wenn mysqldump beendet ist?? Ich habe mit einem Beispiel von Microsoft in C# gespielt, in dem über Process.Starteine kleine Applikation gestartet wird. Sobald die Applikation aufgerufen ist, wird der Prozess beendet. Wenn das auch bei mysqldump so ist, bringt mir die ganze Warterei auf das Prozessende überhaupt nichts...

    Du verwechselst hier den Funktionsaufruf mit der tatsächlichen Prozesslebensdauer. Dein Funktionsaufruf kommt schon nach kurzer Zeit zurück, während der gestartete Prozess beliebig lange laufen kann. Du musst also auf das Prozessende warten und darfst nicht direkt nach dem Funktionsaufruf den nächsten Bearbeitungsschritt ausführen:

    void backup_database()
    {
       Process^ proc_dump = gcnew Process();
       ...
       proc_dump->Start( "mysqldump" );
       proc_dump->WaitForExit();
    
       Process^ proc_archive = gcnew Process();
       ...
       proc_archive ->Start( "7zip" );
       proc_archive ->WaitForExit();
    
       std::remove( "mysqldump.sql" );
    }
    

    Hat den Nachteil, dass unendlich lange auf ein Prozessende gewartet wird. Wenn ein Prozess aus irgendwelchen Gründen stehenbleibt (Meldungsfenster oÄ), dann bleibt die Anwendung so lange stehen, bis jemand das Fenster wegklickt.

    Das sind auch prima Sachen, die man mit einem Debugger beobachten kann. Einfach mal schrittweise durch die Funktion debuggen, dann bekommt man auch ein Gefühl dafür, was wie lange dauert.



  • @DocShow: Das Exited-Ereignis benötigt keine Nachrichtenschleife, denn es funktioniert auch in reinen Konsolenanwendungen (s.a. Beispiel in der Doku).

    Man kann aber auch eine Zeit bei WaitForExit(...) angeben und den Rückgabetyp dann überprüfen.



  • @Th69 sagte in Erstellen eines Datenbankdumps:

    @DocShow: Das Exited-Ereignis benötigt keine Nachrichtenschleife, denn es funktioniert auch in reinen Konsolenanwendungen (s.a. Beispiel in der Doku).

    Das schließt sich doch nicht aus. Ich bin mir da nicht so sicher, dass da im Hintergrund nicht doch irgendwas passiert. Irgendeine Instanz muss doch feststellen, dass der Prozess beendet wird und das auch iwie kommunizieren.



  • Es wird, wie ich oben schon vorgeschlagen habe, ein WaitHandle dafür benutzt (hier ein ProcessWaitHandle mit Callback), s. Process.EnsureWatchingForExit.



  • @Th69
    Ahjo, wir haben etwas aneinander vorbeigeredet. Ich dachte, du meinst, dass das Exited Ereignis immer ausgelöst wird und ich dachte, dass ohne weitere Funktionsaufrufe eine Message Queue benötigt wird. Auf jeden Fall wird in WaitForExit das Ereignis ausgelöst, wenn der Prozess innerhalb der Wartefrist endet, egal ob mit oder ohne Message Queue.



  • @DocShoe / Th69

    ...so ganz habe ich zwar nicht alles verstanden, außer dass WaitForExit funktionieren sollte.
    Wenn ich das so einfüge wie von Dir, DocShoe, beschrieben, also zwei verschiedene Prozesse starte und nach jedem ein WaitForExit() bzw WaitForExit(500)einbaue, dann wird nur der erste Prozess proc_dump ausgeführt. proc_archive und std::remove(...) wird nicht mehr ausgeführt.
    Leider habe ich das mit dem Debuggen noch nicht hinbekommen, werde mich aber nochmals damit beschäftigen...



  • @uvbey
    ...also, ich habe das Programm Windows-Debug-Modus gestartet. Ohne WaitForExitläuft das ohne eine Meldung durch, mit dem WaitForExitwird eine Exception in Process.cs ausgelöst:

    private void EnsureState(State state)
    {
             ...
    	if ((state & State.Associated) != 0 && !Associated)
    	{
    		throw new InvalidOperationException(SR.GetString("NoAssociatedProcess"));
    	}
            ...
    

    --> Diesem Objekt ist kein Prozess zugeordnet ... Das verstehe ich jetzt nicht, wenn ich doch proc_dump->WaitForExit(500); angebe...

    Nachtrag: Die gleiche Exception wird geworfen bei der Abfrage von proc_dump->HasExited.
    Nachtrag 2: Auch wenn ich backup_database() aus dem laufenden Programm mittels Tastendruck aufrufe, erscheint der identische Fehler : NoAssociatedProcess



  • Da bist du jetzt auf eine Besonderheit bei C++ reingefallen, die bei C# sofort zu einem Compilerfehler geführt hätte: Start ist eine statische Methode der Klasse Process und du rufst sie über ein Objekt auf, anstatt den Rückgabewert zu benutzen:

    Process^ proc_dump = Process::Start( "mysqldump" ); // in C# benutzt man Process.Start(...)
    proc_dump->WaitForExit();
    

    Edit:
    Jetzt ist auch klar, warum StartInfo->CreateNoWindow = true; bei dir nicht funktioniert, da du es auf dem falschen Process-Objekt anwendest: du mußt die Überladung Start(ProcessStartInfo) benutzen...

    Alternativ gibt es noch die (eine) nicht-statische Start()-Methode ohne Parameter (schau dir das Beispiel an).

    PS:
    Du kannst in der .NET-Doku auch die Sprache (oben in der Titelzeile über eine ComboBox) von C# auf C++ stellen, um die Beispiele in C++/CLI angezeigt zu bekommen.



  • Dafür kann uvbey nichts, das habe ich verbockt.
    Mea culpa 😞



  • @DocShoe
    ..und ich hab's einfach übernommen - die Verantwortung liegt also bei mir 😉

    @Th69
    ...und wieder bestätigt sich die alte Entwicklerweisheit: Kaum macht man alles richtig, schon geht's!

    Das ist jetzt mein funktionierender Code:

    void backup_database(void)
    {
            ...
    
            Process^ proc_dump = gcnew Process();
    	ProcessStartInfo^ startInfoDump = gcnew ProcessStartInfo("mysqldump");
    	startInfoDump->WindowStyle = ProcessWindowStyle::Hidden;
    	startInfoDump->Arguments = mysqldump_arguments;
    	proc_dump = Process::Start(startInfoDump);
    	proc_dump->WaitForExit(1000);
    
    	Process^ proc_archive = gcnew Process();
    	ProcessStartInfo^ startInfoArchive = gcnew ProcessStartInfo("7z");
    	startInfoArchive->WindowStyle = ProcessWindowStyle::Hidden;
    	startInfoArchive->Arguments = zip_arguments;
    	proc_archive = Process::Start(startInfoArchive);
    	proc_archive->WaitForExit(1000);
    
    	std::remove(dump_filename.c_str());
            return;
    }
    

    Das Cmd-Fenster ist nun nicht mehr sichtbar, und alles funktioniert bestens!

    Vielen herzlichen Dank euch beiden - ich war schon am verzweifeln... Und auch der Hinweis mit der Umstellung auf C++ bei.NET hat mit geholfen und wird mir in Zukunft noch mehr helfen.

    Schönen Tag noch, und dann ein schönes Wochenende

    Viktor


Anmelden zum Antworten