Skip to content

Commit 83ec685

Browse files
authored
Merge pull request #103 from factset/fix/backup-chain/next-log-backup
fix(backup-chain): next log backup can have the same `LastLsn`
2 parents 2152eb4 + e274c99 commit 83ec685

2 files changed

Lines changed: 82 additions & 69 deletions

File tree

src/BackupChain.cs

Lines changed: 63 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
namespace AgDatabaseMove
2-
{
3-
using System.Collections.Generic;
4-
using System.Linq;
5-
using Exceptions;
6-
using SmoFacade;
7-
8-
1+
namespace AgDatabaseMove
2+
{
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using Exceptions;
6+
using SmoFacade;
7+
8+
99
public interface IBackupChain
1010
{
1111
IEnumerable<BackupMetadata> OrderedBackups { get; }
@@ -14,11 +14,11 @@ public interface IBackupChain
1414
/// <summary>
1515
/// Encapsulates the logic for determining the order to apply recent backups.
1616
/// </summary>
17-
public class BackupChain : IBackupChain
18-
{
19-
private readonly IList<BackupMetadata> _orderedBackups;
20-
21-
// This also handles any striped backups
17+
public class BackupChain : IBackupChain
18+
{
19+
private readonly IList<BackupMetadata> _orderedBackups;
20+
21+
// This also handles any striped backups
2222
private BackupChain(IList<BackupMetadata> recentBackups)
2323
{
2424
if(recentBackups == null || recentBackups.Count == 0)
@@ -54,51 +54,53 @@ public BackupChain(Database database) : this(database.MostRecentBackupChain()) {
5454
/// <summary>
5555
/// Backups ordered to have a full restore chain.
5656
/// </summary>
57-
public IEnumerable<BackupMetadata> OrderedBackups => _orderedBackups;
58-
59-
private static IEnumerable<BackupMetadata> MostRecentFullBackup(IEnumerable<BackupMetadata> backups)
60-
{
61-
var fullBackupsOrdered = backups
62-
.Where(b => b.BackupType == BackupFileTools.BackupType.Full)
63-
.OrderByDescending(d => d.CheckpointLsn).ToList();
64-
65-
if(!fullBackupsOrdered.Any())
66-
throw new BackupChainException("Could not find any full backups");
67-
68-
var targetCheckpointLsn = fullBackupsOrdered.First().CheckpointLsn;
69-
// get all the stripes of this backup
70-
return fullBackupsOrdered.Where(fullBackup => fullBackup.CheckpointLsn == targetCheckpointLsn);
71-
}
72-
73-
private static IEnumerable<BackupMetadata> MostRecentDiffBackup(IEnumerable<BackupMetadata> backups,
74-
BackupMetadata lastFullBackup)
75-
{
76-
var diffBackupsOrdered = backups
77-
.Where(b =>
78-
b.BackupType == BackupFileTools.BackupType.Diff &&
79-
b.DatabaseBackupLsn == lastFullBackup.CheckpointLsn)
80-
.OrderByDescending(b => b.LastLsn).ToList();
81-
82-
if(!diffBackupsOrdered.Any())
83-
return new List<BackupMetadata>();
84-
85-
var targetLastLsn = diffBackupsOrdered.First().LastLsn;
86-
// get all the stripes of this backup
87-
return diffBackupsOrdered.Where(diffBackup => diffBackup.LastLsn == targetLastLsn);
88-
}
89-
90-
private static IEnumerable<BackupMetadata> NextLogBackup(IEnumerable<BackupMetadata> backups,
91-
BackupMetadata prevBackup)
92-
{
93-
// also gets all the stripes of the next backup
94-
return backups.Where(b => b.BackupType == BackupFileTools.BackupType.Log &&
95-
prevBackup.LastLsn >= b.FirstLsn && prevBackup.LastLsn + 1 < b.LastLsn);
96-
}
97-
98-
private static bool IsValidFilePath(BackupMetadata meta)
99-
{
100-
var path = meta.PhysicalDeviceName;
101-
return BackupFileTools.IsValidFileUrl(path) || BackupFileTools.IsValidFilePath(path);
102-
}
103-
}
104-
}
57+
public IEnumerable<BackupMetadata> OrderedBackups => _orderedBackups;
58+
59+
private static IEnumerable<BackupMetadata> MostRecentFullBackup(IEnumerable<BackupMetadata> backups)
60+
{
61+
var fullBackupsOrdered = backups
62+
.Where(b => b.BackupType == BackupFileTools.BackupType.Full)
63+
.OrderByDescending(d => d.CheckpointLsn).ToList();
64+
65+
if(!fullBackupsOrdered.Any())
66+
throw new BackupChainException("Could not find any full backups");
67+
68+
var targetCheckpointLsn = fullBackupsOrdered.First().CheckpointLsn;
69+
// get all the stripes of this backup
70+
return fullBackupsOrdered.Where(fullBackup => fullBackup.CheckpointLsn == targetCheckpointLsn);
71+
}
72+
73+
private static IEnumerable<BackupMetadata> MostRecentDiffBackup(IEnumerable<BackupMetadata> backups,
74+
BackupMetadata lastFullBackup)
75+
{
76+
var diffBackupsOrdered = backups
77+
.Where(b =>
78+
b.BackupType == BackupFileTools.BackupType.Diff &&
79+
b.DatabaseBackupLsn == lastFullBackup.CheckpointLsn)
80+
.OrderByDescending(b => b.LastLsn).ToList();
81+
82+
if(!diffBackupsOrdered.Any())
83+
return new List<BackupMetadata>();
84+
85+
var targetLastLsn = diffBackupsOrdered.First().LastLsn;
86+
// get all the stripes of this backup
87+
return diffBackupsOrdered.Where(diffBackup => diffBackup.LastLsn == targetLastLsn);
88+
}
89+
90+
private static IEnumerable<BackupMetadata> NextLogBackup(IEnumerable<BackupMetadata> backups,
91+
BackupMetadata prevBackup)
92+
{
93+
// also gets all the stripes of the next backup
94+
return backups.Where(b => b.BackupType == BackupFileTools.BackupType.Log &&
95+
prevBackup.LastLsn >= b.FirstLsn &&
96+
prevBackup.LastLsn <= b.LastLsn &&
97+
!new BackupMetadataEqualityComparer().EqualsExceptForPhysicalDeviceName(prevBackup, b));
98+
}
99+
100+
private static bool IsValidFilePath(BackupMetadata meta)
101+
{
102+
var path = meta.PhysicalDeviceName;
103+
return BackupFileTools.IsValidFileUrl(path) || BackupFileTools.IsValidFilePath(path);
104+
}
105+
}
106+
}

src/BackupMetadata.cs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,30 @@ namespace AgDatabaseMove
44
using System.Collections.Generic;
55
using SmoFacade;
66

7-
8-
/// <summary>
9-
/// Occasionally we wind up with the same entry for a backup on multiple instance's msdb.
10-
/// For now we'll consider these backups to be equal despite their file location,
11-
/// but perhaps there's value in being able to look for the file in multiple locations.
12-
/// </summary>
137
public class BackupMetadataEqualityComparer : IEqualityComparer<BackupMetadata>
148
{
15-
public bool Equals(BackupMetadata x, BackupMetadata y)
9+
/// <summary>
10+
/// This is used for checking similar backups (like striped backups)
11+
/// </summary>
12+
/// <returns>bool</returns>
13+
public bool EqualsExceptForPhysicalDeviceName(BackupMetadata x, BackupMetadata y)
1614
{
1715
return x.LastLsn == y.LastLsn &&
1816
x.FirstLsn == y.FirstLsn &&
1917
x.BackupType == y.BackupType &&
2018
x.DatabaseName == y.DatabaseName &&
21-
x.PhysicalDeviceName == y.PhysicalDeviceName;
19+
x.CheckpointLsn == y.CheckpointLsn &&
20+
x.DatabaseBackupLsn == x.DatabaseBackupLsn;
21+
}
22+
23+
/// <summary>
24+
/// This is used for checking exactly the same backup (like finding duplicates)
25+
/// </summary>
26+
/// <returns>bool</returns>
27+
public bool Equals(BackupMetadata x, BackupMetadata y)
28+
{
29+
return EqualsExceptForPhysicalDeviceName(x, y)
30+
&& x.PhysicalDeviceName == y.PhysicalDeviceName;
2231
}
2332

2433
public int GetHashCode(BackupMetadata obj)
@@ -30,6 +39,8 @@ public int GetHashCode(BackupMetadata obj)
3039
EqualityComparer<BackupFileTools.BackupType>.Default.GetHashCode(obj.BackupType);
3140
hashCode = hashCode * -1521134295 + obj.FirstLsn.GetHashCode();
3241
hashCode = hashCode * -1521134295 + obj.LastLsn.GetHashCode();
42+
hashCode = hashCode * -1521134295 + obj.CheckpointLsn.GetHashCode();
43+
hashCode = hashCode * -1521134295 + obj.DatabaseBackupLsn.GetHashCode();
3344
return hashCode;
3445
}
3546
}

0 commit comments

Comments
 (0)