Source/NuGetGallery.Operations/Tasks/CopyDatabaseBackupTask.cs (103 lines of code) (raw):

using System; using System.Data.SqlClient; using System.Threading; using AnglicanGeek.DbExecutor; using NuGetGallery.Operations.Common; namespace NuGetGallery.Operations { [Command("copydatabasebackup", "Copy the specified database backup from the source to the destination", AltName = "cdb", MaxArgs=0)] public class CopyDatabaseBackupTask : OpsTask { [Option("Connection string to the source database server", AltName = "s")] public SqlConnectionStringBuilder SourceConnectionString { get; set; } [Option("Connection string to the destination database server", AltName = "d")] public SqlConnectionStringBuilder DestinationConnectionString { get; set; } [Option("Name of the backup file", AltName = "n")] public string BackupName { get; set; } public override void ValidateArguments() { base.ValidateArguments(); if (CurrentEnvironment != null) { if (DestinationConnectionString == null) { DestinationConnectionString = CurrentEnvironment.MainDatabase; } } ArgCheck.Required(SourceConnectionString, "SourceConnectionString"); ArgCheck.RequiredOrConfig(DestinationConnectionString, "DestinationConnectionString"); ArgCheck.Required(BackupName, "BackupName"); } public override void ExecuteCommand() { using (var destinationConnection = new SqlConnection(Util.GetMasterConnectionString(DestinationConnectionString.ConnectionString))) using (var destinationDbExecutor = new SqlExecutor(destinationConnection)) { string sourceDbServerName = Util.GetDatabaseServerName(SourceConnectionString); string destinationDbServerName = Util.GetDatabaseServerName(DestinationConnectionString); destinationConnection.Open(); var copyDbName = string.Format("CopyOf{0}", BackupName); var existingDatabaseBackup = Util.GetDatabase( destinationDbExecutor, copyDbName); if (existingDatabaseBackup != null && existingDatabaseBackup.State == Util.OnlineState) { Log.Info("Skipping {0}. It already exists on {1} and is online.", copyDbName, destinationDbServerName); return; } if (existingDatabaseBackup == null) { StartBackupCopy( destinationDbExecutor, sourceDbServerName, destinationDbServerName, BackupName, copyDbName); Log.Trace("Waiting 15 minutes for copy of {0} from {1} to {2} to complete.", BackupName, sourceDbServerName, destinationDbServerName); if (!WhatIf) { Thread.Sleep(15 * 60 * 1000); } } WaitForBackupCopy( destinationDbExecutor, destinationDbServerName, copyDbName); } } private void StartBackupCopy( IDbExecutor dbExecutor, string sourceDbServerName, string destinationDbServerName, string sourceDbName, string copyDbName) { Log.Trace("Starting copy of {0} from {1} to {2}.", sourceDbName, sourceDbServerName, destinationDbServerName); if (!WhatIf) { var sql = string.Format("CREATE DATABASE {0} AS COPY OF {1}.{2}", copyDbName, sourceDbServerName, sourceDbName); dbExecutor.Execute(sql); } Log.Info("Copying {0} from {1} to {2}.", sourceDbName, sourceDbServerName, destinationDbServerName); } private void WaitForBackupCopy( SqlExecutor dbExecutor, string destinationDbServerName, string copyDbName) { var timeToGiveUp = DateTime.UtcNow.AddHours(1).AddSeconds(30); while (DateTime.UtcNow < timeToGiveUp) { if (WhatIf || Util.DatabaseExistsAndIsOnline( dbExecutor, copyDbName)) { Log.Info("Copied {0} to {1}.", copyDbName, destinationDbServerName); return; } Log.Trace("Database {0} on {1} is not yet ready and online. Waiting for 5 minutes (will give up in {2} minutes).", copyDbName, destinationDbServerName, Math.Round(timeToGiveUp.Subtract(DateTime.UtcNow).TotalMinutes)); Thread.Sleep(5 * 60 * 1000); } } } }