Wir haben eine Anwendung, die eine Mischung aus schnellen (<1 Sekunde) und langsamen Datenbankmigrationen (> 30 Sekunden) bietet. Momentan führen wir Datenbankmigrationen als Teil von CI durch, aber dann muss unser CI-Tool alle Datenbankverbindungszeichenfolgen für unsere App (in mehreren Umgebungen) kennen, was nicht ideal ist. Wir möchten diesen Prozess so ändern, dass die Anwendung beim Start ihre eigenen Datenbankmigrationen ausführt.
Hier ist die Situation:
Wir haben mehrere Instanzen dieser Anwendung - ungefähr 5 in der Produktion. Nennen wir sie node1, ..., node5
. Jede App stellt eine Verbindung zu einer einzelnen SQL Server-Instanz her, und wir verwenden keine fortlaufenden Bereitstellungen (soweit ich weiß, werden alle Apps gleichzeitig bereitgestellt).
Problem: Angenommen, wir haben eine lange laufende Migration. In diesem Fall wird node1
die Migration gestartet und anschließend ausgeführt. Startet jetzt node4
und die lang laufende Migration ist noch nicht abgeschlossen. Startet also node4
auch die Migration -> mögliche Datenbeschädigung? Wie würden Sie dieses Problem verhindern oder ist das Problem überhaupt wichtig genug, um sich Sorgen zu machen?
Ich dachte daran, dieses Problem mit einer verteilten Sperre zu lösen (mit etcd
oder etwas in dieser Richtung). Grundsätzlich versuchen alle Apps, die Sperre zu erlangen. Nur eine von ihnen erhält sie und führt die Migrationen aus und entsperrt sie dann. Wenn der Rest der Apps gestartet wird und in den kritischen Bereich wechselt, wurden bereits alle Migrationen ausgeführt, sodass das Migrationsskript nur beendet wird.
Mein Bauch sagt jedoch: "Das ist übertrieben, es muss eine einfachere Lösung geben." Also dachte ich mir, ich würde hier fragen, ob jemand andere bessere Ideen hat.
Antworten:
Da Sie SQL Server erwähnt haben: Gemäß diesem früheren DBA.SE-Beitrag können (und sollten) Schemaänderungen in Transaktionen übernommen werden. Auf diese Weise können Sie Ihre Migrationen wie jede andere Form von gleichzeitigen Schreibvorgängen in Ihre Datenbank gestalten. Sie starten eine Transaktion und setzen sie zurück, wenn sie fehlschlägt. Dies verhindert zumindest einige der schlimmsten Datenbankbeschädigungsszenarien (obwohl Transaktionen allein keinen Datenverlust verhindern, wenn destruktive Migrationsschritte wie das Löschen einer Spalte oder Tabelle ausgeführt werden).
Bisher benötigen Sie sicher auch eine
migrations
Tabelle, in der bereits angewendete Migrationen registriert sind, damit ein Anwendungsprozess prüfen kann, ob eine bestimmte Migration bereits angewendet wurde oder nicht. Verwenden Sie dann "SELECT FOR UPDATE", um Ihre Migrationen wie folgt zu implementieren (Pseudocode):SELECT FROM Migrations FOR UPDATE WHERE MigrationLabel='MyMigration42'
INSERT 'MyMigration42' INTO Migrations(MigrationLabel)
Dadurch wird der Sperrmechanismus direkt in den Test " Wurde die Migration bereits angewendet" integriert .
Beachten Sie, dass dieses Design theoretisch ermöglicht, dass Ihre Migrationsschritte nicht wissen, welche Anwendung sie tatsächlich anwendet. Möglicherweise wird Schritt 1 von App1, Schritt 2 von App2, Schritt 3 von App 3, Schritt 4 von App1 angewendet wieder und so weiter. Es ist jedoch auch eine gute Idee, keine Migrationen anzuwenden, solange andere App-Instanzen verwendet werden. Die parallele Bereitstellung, wie in Ihrer Frage erwähnt, kann diese Einschränkung bereits berücksichtigen.
quelle
Möglicherweise finden Sie eine Bibliothek, die die Datenbankmigration mit mehreren Knoten unterstützt.
Ich kenne zwei Bibliotheken in der Java-Welt, die beide unterstützen, was Sie brauchen:
Es gibt wahrscheinlich auch andere Tools für Java und andere Sprachen.
Wenn Sie ein solches Tool nicht verwenden können (oder möchten), kann eine Tabelle als Sperre oder sogar als Migrationsprotokoll verwendet werden. Ein Beispiel finden Sie in der Antwort von Doc Browns .
quelle