From 83d8b2032639e29c5856cf05fe93901c2dad0c03 Mon Sep 17 00:00:00 2001 From: AdityaNPL Date: Tue, 16 Feb 2021 12:17:57 +0000 Subject: [PATCH 1/4] feat(ignore_copy_only_backups): Added in the option to ignore copy_only backups --- src/AgDatabase.cs | 6 +++--- src/BackupChain.cs | 4 ++-- src/SmoFacade/Database.cs | 13 ++++++++++--- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/AgDatabase.cs b/src/AgDatabase.cs index 0bb435c..132a5fb 100755 --- a/src/AgDatabase.cs +++ b/src/AgDatabase.cs @@ -23,7 +23,7 @@ public interface IAgDatabase bool Exists(); void Delete(); void LogBackup(); - List RecentBackups(); + List RecentBackups(bool ignoreCopyOnlyBackups=false); void JoinAg(); void Restore(IEnumerable backupOrder, Func fileRelocation = null); @@ -109,10 +109,10 @@ public void Restore(IEnumerable backupOrder, Func /// Builds a list of recent backups from msdb on each AG instance. /// - public List RecentBackups() + public List RecentBackups(bool ignoreCopyOnlyBackups=false) { var bag = new ConcurrentBag(); - _listener.ForEachAgInstance(s => s.Database(Name).RecentBackups().ForEach(backup => bag.Add(backup))); + _listener.ForEachAgInstance(s => s.Database(Name).RecentBackups(ignoreCopyOnlyBackups).ForEach(backup => bag.Add(backup))); return bag.ToList(); } diff --git a/src/BackupChain.cs b/src/BackupChain.cs index 95096ea..5bc814a 100755 --- a/src/BackupChain.cs +++ b/src/BackupChain.cs @@ -44,12 +44,12 @@ private BackupChain(IList recentBackups) /// /// Initializes a backup chain from a database that is part of an AG. /// - public BackupChain(IAgDatabase agDatabase) : this(agDatabase.RecentBackups()) { } + public BackupChain(IAgDatabase agDatabase, bool ignoreCopyOnlyBackups = false) : this(agDatabase.RecentBackups(ignoreCopyOnlyBackups)) { } /// /// Initializes a backup chain from a stand alone database that is not part of an AG. /// - public BackupChain(Database database) : this(database.RecentBackups()) { } + public BackupChain(Database database, bool ignoreCopyOnlyBackups=false) : this(database.RecentBackups(ignoreCopyOnlyBackups)) { } /// /// Backups ordered to have a full restore chain. diff --git a/src/SmoFacade/Database.cs b/src/SmoFacade/Database.cs index beba925..36115e3 100755 --- a/src/SmoFacade/Database.cs +++ b/src/SmoFacade/Database.cs @@ -62,7 +62,7 @@ public void Drop() /// Queries msdb on the instance for backups of this database. /// /// A list of backups known by msdb - public List RecentBackups() + public List RecentBackups(bool ignoreCopyOnlyBackups=false) { var backups = new List(); @@ -70,8 +70,15 @@ public List RecentBackups() "s.database_backup_lsn, s.checkpoint_lsn, s.[type] AS backup_type, s.server_name, s.recovery_model " + "FROM msdb.dbo.backupset s " + "INNER JOIN msdb.dbo.backupmediafamily m ON s.media_set_id = m.media_set_id " + - "WHERE s.last_lsn >= (SELECT MAX(last_lsn) FROM msdb.dbo.backupset WHERE [type] = 'D' and database_name = @dbName) " + - "AND s.database_name = @dbName ORDER BY s.backup_start_date DESC, backup_finish_date"; + "WHERE s.last_lsn >= (" + + "SELECT MAX(last_lsn) FROM msdb.dbo.backupset " + + "WHERE [type] = 'D' " + + "AND database_name = @dbName " + + $"{(ignoreCopyOnlyBackups ? "AND is_copy_only = 0" : "")}" + + ") " + + "AND s.database_name = @dbName " + + $"{(ignoreCopyOnlyBackups ? "AND is_copy_only = 0" : "")}" + + "ORDER BY s.backup_start_date DESC, backup_finish_date"; using var cmd = _server.SqlConnection.CreateCommand(); cmd.CommandText = query; From 7fd2853498e182517a47464450a3cc59d0ea4eb5 Mon Sep 17 00:00:00 2001 From: AdityaNPL Date: Tue, 16 Feb 2021 12:26:53 +0000 Subject: [PATCH 2/4] fix(tests): resolving CS0854 error 'Error CS0854 An expression tree may not contain a call or invocation that uses optional arguments' --- tests/AgDatabaseMove.Unit/BackupOrder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/AgDatabaseMove.Unit/BackupOrder.cs b/tests/AgDatabaseMove.Unit/BackupOrder.cs index b97e321..f13d54b 100755 --- a/tests/AgDatabaseMove.Unit/BackupOrder.cs +++ b/tests/AgDatabaseMove.Unit/BackupOrder.cs @@ -72,7 +72,7 @@ private static List ListBackups() public void BackupChainOrdered() { var agDatabase = new Mock(); - agDatabase.Setup(agd => agd.RecentBackups()).Returns(_listBackups); + agDatabase.Setup(agd => agd.RecentBackups(false)).Returns(_listBackups); var backupChain = new BackupChain(agDatabase.Object); var expected = _listBackups.OrderBy(bu => bu.FirstLsn); @@ -85,7 +85,7 @@ public void MissingLink() { var backups = ListBackups().Where(b => b.FirstLsn != 126000000955200001).ToList(); var agDatabase = new Mock(); - agDatabase.Setup(agd => agd.RecentBackups()).Returns(backups); + agDatabase.Setup(agd => agd.RecentBackups(false)).Returns(backups); var chain = new BackupChain(agDatabase.Object).OrderedBackups; Assert.NotEqual(chain.Last().LastLsn, ListBackups().Max(b => b.LastLsn)); From ad14a30ca777de1e2ff84469892e4c8a7e8141a0 Mon Sep 17 00:00:00 2001 From: AdityaNPL Date: Thu, 18 Feb 2021 15:38:37 +0000 Subject: [PATCH 3/4] feat(ignore_copy_only_backups): Added a docstring for the `ignoreCopyOnlyBackups` parameter --- src/SmoFacade/Database.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/SmoFacade/Database.cs b/src/SmoFacade/Database.cs index 36115e3..6597944 100755 --- a/src/SmoFacade/Database.cs +++ b/src/SmoFacade/Database.cs @@ -61,6 +61,11 @@ public void Drop() /// /// Queries msdb on the instance for backups of this database. /// + /// + /// Setting this to `true` will ignore copy_only backups + /// These are temp backups made for a specific use case and should not be part of the backup-chain for general restore processes + /// For more info: https://docs.microsoft.com/sql/relational-databases/backup-restore/copy-only-backups-sql-server + /// /// A list of backups known by msdb public List RecentBackups(bool ignoreCopyOnlyBackups=false) { From 62941034cd49c5578a37907e0a06f8965ac8ac84 Mon Sep 17 00:00:00 2001 From: AdityaNPL Date: Tue, 23 Feb 2021 11:14:13 +0000 Subject: [PATCH 4/4] deprecate(copy_only): removing feature to choose to add copy_only backups to chain --- src/AgDatabase.cs | 6 +++--- src/BackupChain.cs | 4 ++-- src/SmoFacade/Database.cs | 11 +++-------- tests/AgDatabaseMove.Unit/BackupOrder.cs | 4 ++-- 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/AgDatabase.cs b/src/AgDatabase.cs index 132a5fb..0bb435c 100755 --- a/src/AgDatabase.cs +++ b/src/AgDatabase.cs @@ -23,7 +23,7 @@ public interface IAgDatabase bool Exists(); void Delete(); void LogBackup(); - List RecentBackups(bool ignoreCopyOnlyBackups=false); + List RecentBackups(); void JoinAg(); void Restore(IEnumerable backupOrder, Func fileRelocation = null); @@ -109,10 +109,10 @@ public void Restore(IEnumerable backupOrder, Func /// Builds a list of recent backups from msdb on each AG instance. /// - public List RecentBackups(bool ignoreCopyOnlyBackups=false) + public List RecentBackups() { var bag = new ConcurrentBag(); - _listener.ForEachAgInstance(s => s.Database(Name).RecentBackups(ignoreCopyOnlyBackups).ForEach(backup => bag.Add(backup))); + _listener.ForEachAgInstance(s => s.Database(Name).RecentBackups().ForEach(backup => bag.Add(backup))); return bag.ToList(); } diff --git a/src/BackupChain.cs b/src/BackupChain.cs index 5bc814a..95096ea 100755 --- a/src/BackupChain.cs +++ b/src/BackupChain.cs @@ -44,12 +44,12 @@ private BackupChain(IList recentBackups) /// /// Initializes a backup chain from a database that is part of an AG. /// - public BackupChain(IAgDatabase agDatabase, bool ignoreCopyOnlyBackups = false) : this(agDatabase.RecentBackups(ignoreCopyOnlyBackups)) { } + public BackupChain(IAgDatabase agDatabase) : this(agDatabase.RecentBackups()) { } /// /// Initializes a backup chain from a stand alone database that is not part of an AG. /// - public BackupChain(Database database, bool ignoreCopyOnlyBackups=false) : this(database.RecentBackups(ignoreCopyOnlyBackups)) { } + public BackupChain(Database database) : this(database.RecentBackups()) { } /// /// Backups ordered to have a full restore chain. diff --git a/src/SmoFacade/Database.cs b/src/SmoFacade/Database.cs index 6597944..5c0c8ec 100755 --- a/src/SmoFacade/Database.cs +++ b/src/SmoFacade/Database.cs @@ -61,13 +61,8 @@ public void Drop() /// /// Queries msdb on the instance for backups of this database. /// - /// - /// Setting this to `true` will ignore copy_only backups - /// These are temp backups made for a specific use case and should not be part of the backup-chain for general restore processes - /// For more info: https://docs.microsoft.com/sql/relational-databases/backup-restore/copy-only-backups-sql-server - /// /// A list of backups known by msdb - public List RecentBackups(bool ignoreCopyOnlyBackups=false) + public List RecentBackups() { var backups = new List(); @@ -79,10 +74,10 @@ public List RecentBackups(bool ignoreCopyOnlyBackups=false) "SELECT MAX(last_lsn) FROM msdb.dbo.backupset " + "WHERE [type] = 'D' " + "AND database_name = @dbName " + - $"{(ignoreCopyOnlyBackups ? "AND is_copy_only = 0" : "")}" + + "AND is_copy_only = 0" + ") " + "AND s.database_name = @dbName " + - $"{(ignoreCopyOnlyBackups ? "AND is_copy_only = 0" : "")}" + + "AND is_copy_only = 0" + "ORDER BY s.backup_start_date DESC, backup_finish_date"; using var cmd = _server.SqlConnection.CreateCommand(); diff --git a/tests/AgDatabaseMove.Unit/BackupOrder.cs b/tests/AgDatabaseMove.Unit/BackupOrder.cs index f13d54b..b97e321 100755 --- a/tests/AgDatabaseMove.Unit/BackupOrder.cs +++ b/tests/AgDatabaseMove.Unit/BackupOrder.cs @@ -72,7 +72,7 @@ private static List ListBackups() public void BackupChainOrdered() { var agDatabase = new Mock(); - agDatabase.Setup(agd => agd.RecentBackups(false)).Returns(_listBackups); + agDatabase.Setup(agd => agd.RecentBackups()).Returns(_listBackups); var backupChain = new BackupChain(agDatabase.Object); var expected = _listBackups.OrderBy(bu => bu.FirstLsn); @@ -85,7 +85,7 @@ public void MissingLink() { var backups = ListBackups().Where(b => b.FirstLsn != 126000000955200001).ToList(); var agDatabase = new Mock(); - agDatabase.Setup(agd => agd.RecentBackups(false)).Returns(backups); + agDatabase.Setup(agd => agd.RecentBackups()).Returns(backups); var chain = new BackupChain(agDatabase.Object).OrderedBackups; Assert.NotEqual(chain.Last().LastLsn, ListBackups().Max(b => b.LastLsn));