Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 63 additions & 61 deletions src/BackupChain.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
namespace AgDatabaseMove
{
using System.Collections.Generic;
using System.Linq;
using Exceptions;
using SmoFacade;


namespace AgDatabaseMove
{
using System.Collections.Generic;
using System.Linq;
using Exceptions;
using SmoFacade;
public interface IBackupChain
{
IEnumerable<BackupMetadata> OrderedBackups { get; }
Expand All @@ -14,11 +14,11 @@ public interface IBackupChain
/// <summary>
/// Encapsulates the logic for determining the order to apply recent backups.
/// </summary>
public class BackupChain : IBackupChain
{
private readonly IList<BackupMetadata> _orderedBackups;

// This also handles any striped backups
public class BackupChain : IBackupChain
{
private readonly IList<BackupMetadata> _orderedBackups;
// This also handles any striped backups
private BackupChain(IList<BackupMetadata> recentBackups)
{
if(recentBackups == null || recentBackups.Count == 0)
Expand Down Expand Up @@ -54,51 +54,53 @@ public BackupChain(Database database) : this(database.MostRecentBackupChain()) {
/// <summary>
/// Backups ordered to have a full restore chain.
/// </summary>
public IEnumerable<BackupMetadata> OrderedBackups => _orderedBackups;

private static IEnumerable<BackupMetadata> MostRecentFullBackup(IEnumerable<BackupMetadata> backups)
{
var fullBackupsOrdered = backups
.Where(b => b.BackupType == BackupFileTools.BackupType.Full)
.OrderByDescending(d => d.CheckpointLsn).ToList();

if(!fullBackupsOrdered.Any())
throw new BackupChainException("Could not find any full backups");

var targetCheckpointLsn = fullBackupsOrdered.First().CheckpointLsn;
// get all the stripes of this backup
return fullBackupsOrdered.Where(fullBackup => fullBackup.CheckpointLsn == targetCheckpointLsn);
}

private static IEnumerable<BackupMetadata> MostRecentDiffBackup(IEnumerable<BackupMetadata> backups,
BackupMetadata lastFullBackup)
{
var diffBackupsOrdered = backups
.Where(b =>
b.BackupType == BackupFileTools.BackupType.Diff &&
b.DatabaseBackupLsn == lastFullBackup.CheckpointLsn)
.OrderByDescending(b => b.LastLsn).ToList();

if(!diffBackupsOrdered.Any())
return new List<BackupMetadata>();

var targetLastLsn = diffBackupsOrdered.First().LastLsn;
// get all the stripes of this backup
return diffBackupsOrdered.Where(diffBackup => diffBackup.LastLsn == targetLastLsn);
}

private static IEnumerable<BackupMetadata> NextLogBackup(IEnumerable<BackupMetadata> backups,
BackupMetadata prevBackup)
{
// also gets all the stripes of the next backup
return backups.Where(b => b.BackupType == BackupFileTools.BackupType.Log &&
prevBackup.LastLsn >= b.FirstLsn && prevBackup.LastLsn + 1 < b.LastLsn);
}

private static bool IsValidFilePath(BackupMetadata meta)
{
var path = meta.PhysicalDeviceName;
return BackupFileTools.IsValidFileUrl(path) || BackupFileTools.IsValidFilePath(path);
}
}
}
public IEnumerable<BackupMetadata> OrderedBackups => _orderedBackups;

private static IEnumerable<BackupMetadata> MostRecentFullBackup(IEnumerable<BackupMetadata> backups)
{
var fullBackupsOrdered = backups
.Where(b => b.BackupType == BackupFileTools.BackupType.Full)
.OrderByDescending(d => d.CheckpointLsn).ToList();

if(!fullBackupsOrdered.Any())
throw new BackupChainException("Could not find any full backups");

var targetCheckpointLsn = fullBackupsOrdered.First().CheckpointLsn;
// get all the stripes of this backup
return fullBackupsOrdered.Where(fullBackup => fullBackup.CheckpointLsn == targetCheckpointLsn);
}

private static IEnumerable<BackupMetadata> MostRecentDiffBackup(IEnumerable<BackupMetadata> backups,
BackupMetadata lastFullBackup)
{
var diffBackupsOrdered = backups
.Where(b =>
b.BackupType == BackupFileTools.BackupType.Diff &&
b.DatabaseBackupLsn == lastFullBackup.CheckpointLsn)
.OrderByDescending(b => b.LastLsn).ToList();

if(!diffBackupsOrdered.Any())
return new List<BackupMetadata>();

var targetLastLsn = diffBackupsOrdered.First().LastLsn;
// get all the stripes of this backup
return diffBackupsOrdered.Where(diffBackup => diffBackup.LastLsn == targetLastLsn);
}

private static IEnumerable<BackupMetadata> NextLogBackup(IEnumerable<BackupMetadata> backups,
BackupMetadata prevBackup)
{
// also gets all the stripes of the next backup
return backups.Where(b => b.BackupType == BackupFileTools.BackupType.Log &&
prevBackup.LastLsn >= b.FirstLsn &&
prevBackup.LastLsn <= b.LastLsn &&
!new BackupMetadataEqualityComparer().EqualsExceptForPhysicalDeviceName(prevBackup, b));
}

private static bool IsValidFilePath(BackupMetadata meta)
{
var path = meta.PhysicalDeviceName;
return BackupFileTools.IsValidFileUrl(path) || BackupFileTools.IsValidFilePath(path);
}
}
}
27 changes: 19 additions & 8 deletions src/BackupMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,30 @@ namespace AgDatabaseMove
using System.Collections.Generic;
using SmoFacade;


/// <summary>
/// Occasionally we wind up with the same entry for a backup on multiple instance's msdb.
/// For now we'll consider these backups to be equal despite their file location,
/// but perhaps there's value in being able to look for the file in multiple locations.
/// </summary>
public class BackupMetadataEqualityComparer : IEqualityComparer<BackupMetadata>
{
public bool Equals(BackupMetadata x, BackupMetadata y)
/// <summary>
/// This is used for checking similar backups (like striped backups)
/// </summary>
/// <returns>bool</returns>
public bool EqualsExceptForPhysicalDeviceName(BackupMetadata x, BackupMetadata y)
{
return x.LastLsn == y.LastLsn &&
x.FirstLsn == y.FirstLsn &&
x.BackupType == y.BackupType &&
x.DatabaseName == y.DatabaseName &&
x.PhysicalDeviceName == y.PhysicalDeviceName;
x.CheckpointLsn == y.CheckpointLsn &&
x.DatabaseBackupLsn == x.DatabaseBackupLsn;
}

/// <summary>
/// This is used for checking exactly the same backup (like finding duplicates)
/// </summary>
/// <returns>bool</returns>
public bool Equals(BackupMetadata x, BackupMetadata y)
{
return EqualsExceptForPhysicalDeviceName(x, y)
&& x.PhysicalDeviceName == y.PhysicalDeviceName;
}

public int GetHashCode(BackupMetadata obj)
Expand All @@ -30,6 +39,8 @@ public int GetHashCode(BackupMetadata obj)
EqualityComparer<BackupFileTools.BackupType>.Default.GetHashCode(obj.BackupType);
hashCode = hashCode * -1521134295 + obj.FirstLsn.GetHashCode();
hashCode = hashCode * -1521134295 + obj.LastLsn.GetHashCode();
hashCode = hashCode * -1521134295 + obj.CheckpointLsn.GetHashCode();
hashCode = hashCode * -1521134295 + obj.DatabaseBackupLsn.GetHashCode();
return hashCode;
}
}
Expand Down