Erstellen eines Datenbankdumps



  • Bzgl. CreateNoWindow (s. Remarks):
    Wenn du noch .NET Framework verwendest, mußt du noch explizit UseShellExecute = false setzen, da dieser Wert sonst standardmäßig gesetzt ist und dann CreateNoWindow ignoriert wird.

    PS: Schau dir auch mal MySqlBackup.Net an - damit kannst du dann aus deinem Programm heraus einen DB-Dump ausführen.



  • @uvbey

    aus meiner Sicht liegt hier ein grundlegender Fehler im Konzept. Also was die Datensicherung betrifft, die kann doch unabhängig von irgendwelchen Programmaufrufen stattfinden. Und wenn sie an ein Programm gekoppelt wird, dann erschließt sich mir nicht warum man dazu mysqldump als externes Programm aufrufen muss verbunden mit einer expliziten Angabe der Zugangsdaten. Denn: In Deinem Programm ist der Zugang ja bereits hergestellt, also hast Du in Deinem Programm Vollzugriff und kannst mit den Daten auch Backup-Dateien anlegen. Und wie wäre es mit einem dedizierten Backup-Server nach dem Master/Slave-Prinzip?

    MFG



  • Was spricht denn gegen einen Windows Task Scheduler Job oder Shutdown Skript?
    Ich würde tatsächlich einen dedizierten Windows-Benutzer anlegen und die Backups über den Task Scheduler/Shutdown Skript mit diesen Benutzer anlegen. Das geschieht dann im Hintergrund, ohne dass jemand anderes davon was mitbekommt. Vielleicht noch eine Log-Datei dazu schreiben, damit man sieht, ob was schiefgelaufen ist.
    Oder du guckst dich mal nach Professionellen Lösungen um, vielleicht bieten die ihr Produkt als Freeware für eingeschränkten Gebrauch an.



  • @Th69 sagte in Erstellen eines Datenbankdumps:

    UseShellExecute = false

    ..das funktioniert leider auch nicht ☹

    Der Vorschlag, aus dem Programm heraus einen dump zu erstellen, ist sicherlich richtig. Allerdings muss ich dann ja noch ein Zip-Programm installieren / einbinden, welches (aus dem Programm heraus) die Daten zippt und die (temporäre ) sql-Datei dann wieder löscht. Ich denke, das übersteigt meine Fähigkeiten und kostet mich auch zu viel Zeit, mir das alles anzueignen (Ich bin noch mit Fortran 4 groß geworden 😉 ).

    @DocShoe: Das mit dem Windows Task Scheduler war übrigens mein erster Ansatz und war bis vor kurzem in Betrieb. Allerdings stellt sich dann wieder die Frage, wie ich mit dem Passwort umgehen soll. Die Herausforderung ist ja, dass sich auf dem Vereinsrechner mehrere Admins tummeln, denen ich nicht unbedingt die Kontrolle über die Datenbank geben möchte. Manche spielen doch gerne etwas herum, ohne dass sie genau wissen, was sie da tun... Und persönliche Bereiche sind auf dem Vereinsrechner unerwünscht, das muss ich respektieren.
    Ich versuche doch mal den Ansatz, einen Dump zu erstellen, dann ihn zu zippen und den Ursprungsdump wieder zu löschen...



  • Moin.
    Ich habe das Programm jetzt wie folgt angepasst:

    Process^ myProcess = gcnew Process();
    myProcess->StartInfo->UseShellExecute = false;
    myProcess->StartInfo->CreateNoWindow = true;
    myProcess->Start("mysqldump", "--user=dumpuser --password=passwd db-name -r pfad/mysqldump_db-name.sql");						// sql-dump erstellen
    Sleep(100);
    myProcess->Start("7z", "a  pfad/mysqldump_db-name.sql.7z  pfad/mysqldump_db-name.sql -ppasswd-7z");							// sql-dump zippen
    Sleep(100);
    std::remove("pfad/mysqldump_db-name.sql");							// sql-dump löschen
    

    Das Endergebnis ist perfekt: Es liegt ein gezippter und mit dem Passwort 'passwd-7z' verschlüsselter Datenbankdump vor. Unschön ist, dass zweimal die Cmd-Fenster kurz auftauchen - auch wenn nichts sicherheitsrelevantes drin steht. Auch funktioniert beispielsweise:

    myProcess->WaitForExit(100);
    

    überhaupt nicht. Anstelle von "Sleep(100)" eingesetzt werden keine weiteren Prozesse mehr gestartet.
    Hat da noch jemand eine Idee?



  • Dass WaitForExit nicht funktioniert wundert mich. Aber mit Sleep( 100 ) pokerst du, dass der Export maximal 100ms braucht, bevor du anfängst, die Dateien einzupacken.
    Was ist, wenn du dich an das Exited Event des Prozesses (EnableRaisingEvents auf true setzen) hängst und dann das Archivieren startest?

    Edit:
    Das kann alles so gar nicht richtig funktionieren. Du startest die drei Aufgaben (dump, archive, delete) im Abstand von 100ms und wartest nicht darauf, dass die vorherige Aufgabe abgeschlossen wird. Du musst schon berücksichtigen, dass Aufgaben länger brauchen und darfst erst dann weitermachen, wenn die Aufgabe abgeschlossen ist.

    Und wenn iwas nicht funktioniert dann bitte beschreib doch bitte das Verhalten, damit man einen Anhaltspunkt hat. "Funktioniert nicht" kann tausend Gründe haben.



  • @DocShoe
    Okay, verstanden: Wenn ich anstelle "Sleep(100)" (wie bekommst Du das eigentlich so schön rot markiert?) WaitForExit(100) einfüge, dann wird der nachfolgende / die nachfolgenden Prozesse nicht mehr ausgeführt. In meinem obigen Beispiel also in Zeile 7: "remove" wird nicht mehr ausgeführt, die Datei "mysqldump_db-name.sql" ist immer noch vorhanden.

    Das mit dem Exited-Event bekomme ich auch nicht hin:

    myProcess->EnableRaisingEvents = true;
    myProcess->Exited += gcnew EventHandler(myProcess_Exited);
    ...
    void myProcess_Exited(System::Object^ sender, System::EventArgs^ e)
    {
    	std::remove("mysqldump.sql");							// sql-dump löschen
    }
    

    Beim Compilieren kommt die Fehlermeldung:

    "myProcess_Exited": nichtdeklarierter Bezeichner
    Fehler in Argument #1 des Delegatkonstruktoraufrufs "System::EventHandler":
    

    Da stehe ich auch gerade auf dem Schlauch ☹


  • Gesperrt

    @uvbey sagte in Erstellen eines Datenbankdumps:

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

    Markdown... mit:

    `inline code`
    

  • 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.


Anmelden zum Antworten