Skip to content

Commit 97d03be

Browse files
Restore-DbaDatabase - Add -StopAtLsn parameter for LSN-based restore (#10245)
1 parent 899cdc3 commit 97d03be

4 files changed

Lines changed: 54 additions & 8 deletions

File tree

public/Invoke-DbaAdvancedRestore.ps1

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,20 @@ function Invoke-DbaAdvancedRestore {
122122
Provides more granular control than timestamp-based recovery for critical business operations.
123123
124124
.PARAMETER StopBefore
125-
Stops the restore operation just before the specified StopMark rather than after it.
126-
Use this when you need to exclude a particular marked transaction from the restored database.
127-
Only effective when used in combination with the StopMark parameter for mark-based recovery scenarios.
125+
Stops the restore operation just before the specified StopMark or StopAtLsn rather than after it.
126+
Use this when you need to exclude a particular marked transaction or LSN from the restored database.
127+
Only effective when used in combination with the StopMark or StopAtLsn parameter.
128128
129129
.PARAMETER StopAfterDate
130130
DateTime value specifying that only StopMark occurrences after this date should be considered for restore termination.
131131
Use this when the same mark name appears multiple times in your transaction log backups.
132132
Ensures the restore stops at the correct instance of the mark when identical mark names exist at different times.
133133
134+
.PARAMETER StopAtLsn
135+
Log Sequence Number (LSN) in the transaction log at which to stop the restore operation.
136+
Use this for precise point-in-time recovery to an exact LSN, which provides more granular control than timestamp-based recovery.
137+
The LSN value can be obtained from sys.fn_dblog, backup headers, or error logs. Combine with -StopBefore to stop just before the specified LSN.
138+
134139
.PARAMETER Checksum
135140
Enables backup checksum verification during restore operations. Forces the restore to verify backup checksums and fail if checksums are not present.
136141
Use this to ensure backup files contain checksums and validate them during restore, following backup best practices.
@@ -245,6 +250,7 @@ function Invoke-DbaAdvancedRestore {
245250
[switch]$StopBefore,
246251
[string]$StopMark,
247252
[datetime]$StopAfterDate,
253+
[string]$StopAtLsn,
248254
[switch]$Checksum,
249255
[switch]$Restart,
250256
[switch]$EnableException
@@ -329,7 +335,13 @@ function Invoke-DbaAdvancedRestore {
329335
} else {
330336
$restore.NoRecovery = $False
331337
}
332-
if (-not [string]::IsNullOrEmpty($StopMark)) {
338+
if (-not [string]::IsNullOrEmpty($StopAtLsn)) {
339+
if ($StopBefore -eq $True) {
340+
$restore.StopBeforeMarkName = "lsn:$StopAtLsn"
341+
} else {
342+
$restore.StopAtMarkName = "lsn:$StopAtLsn"
343+
}
344+
} elseif (-not [string]::IsNullOrEmpty($StopMark)) {
333345
if ($StopBefore -eq $True) {
334346
$restore.StopBeforeMarkName = $StopMark
335347
if ($null -ne $StopAfterDate) {

public/Restore-DbaDatabase.ps1

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ function Restore-DbaDatabase {
166166
Use this for log shipping secondary servers or when you need read-only access during restore operations.
167167
The directory must exist and be writable by the SQL Server service account for undo file creation.
168168
169-
.PARAMETER StorageCredential
169+
.PARAMETER StorageCredential
170170
Specifies the SQL Server credential name for authenticating to Azure blob storage or S3-compatible object storage during restore operations.
171171
Use this when restoring from Azure blob storage or S3 backups that require authentication.
172172
For Azure: The credential must contain valid Azure storage account keys or SAS tokens.
@@ -240,11 +240,16 @@ function Restore-DbaDatabase {
240240
Marked point in the transaction log to stop the restore at (Mark is created via BEGIN TRANSACTION (https://docs.microsoft.com/en-us/sql/t-sql/language-elements/begin-transaction-transact-sql?view=sql-server-ver15)).
241241
242242
.PARAMETER StopBefore
243-
Switch to indicate the restore should stop before StopMark occurs, default is to stop when mark is created.
243+
Switch to indicate the restore should stop before StopMark or StopAtLsn occurs, default is to stop when mark/LSN is reached.
244244
245245
.PARAMETER StopAfterDate
246246
By default the restore will stop at the first occurence of StopMark found in the chain, passing a datetime where will cause it to stop the first StopMark atfer that datetime.
247247
248+
.PARAMETER StopAtLsn
249+
Log Sequence Number (LSN) in the transaction log at which to stop the restore operation.
250+
Use this for precise point-in-time recovery to an exact LSN, which provides more granular control than timestamp-based recovery.
251+
The LSN value can be obtained from sys.fn_dblog, backup headers, or error logs. Combine with -StopBefore to stop just before the specified LSN.
252+
248253
.PARAMETER Checksum
249254
Enables backup checksum verification during restore operations. Forces the restore to verify backup checksums and fail if checksums are not present.
250255
Use this to ensure backup files contain checksums and validate them during restore, following backup best practices.
@@ -435,6 +440,16 @@ function Restore-DbaDatabase {
435440
436441
Restores the backups from \\ServerName\ShareName\File as database, stops before the first 'OvernightStart' mark that occurs after '21:00 10/05/2020'.
437442
443+
.EXAMPLE
444+
PS C:\> Restore-DbaDatabase -SqlInstance server1 -Path \\ServerName\ShareName\File -DatabaseName database -StopAtLsn '00000030:00000f28:0001'
445+
446+
Restores the backups from \\ServerName\ShareName\File as database, stopping when the specified LSN is reached.
447+
448+
.EXAMPLE
449+
PS C:\> Restore-DbaDatabase -SqlInstance server1 -Path \\ServerName\ShareName\File -DatabaseName database -StopAtLsn '00000030:00000f28:0001' -StopBefore
450+
451+
Restores the backups from \\ServerName\ShareName\File as database, stopping just before the specified LSN is reached.
452+
438453
Note that Date time needs to be specified in your local SQL Server culture
439454
440455
.EXAMPLE
@@ -522,6 +537,7 @@ function Restore-DbaDatabase {
522537
[switch]$StopBefore,
523538
[string]$StopMark,
524539
[datetime]$StopAfterDate = (Get-Date '01/01/1971'),
540+
[string]$StopAtLsn,
525541
[int]$StatementTimeout = 0,
526542
[parameter(ParameterSetName = "Restore")][parameter(ParameterSetName = "RestorePage")][switch]$Checksum,
527543
[parameter(ParameterSetName = "Restore")][parameter(ParameterSetName = "RestorePage")][switch]$Restart
@@ -916,6 +932,7 @@ function Restore-DbaDatabase {
916932
StopMark = $StopMark
917933
StopAfterDate = $StopAfterDate
918934
StopBefore = $StopBefore
935+
StopAtLsn = $StopAtLsn
919936
ExecuteAs = $ExecuteAs
920937
Checksum = $Checksum
921938
Restart = $Restart

tests/Invoke-DbaAdvancedRestore.Tests.ps1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Describe $CommandName -Tag UnitTests {
3535
"StopAfterDate",
3636
"Checksum",
3737
"Restart",
38+
"StopAtLsn",
3839
"EnableException"
3940
)
4041
Compare-Object -ReferenceObject $expectedParameters -DifferenceObject $hasParameters | Should -BeNullOrEmpty

tests/Restore-DbaDatabase.Tests.ps1

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ Describe $CommandName -Tag UnitTests {
6464
"ExecuteAs",
6565
"Checksum",
6666
"Restart",
67-
"NoXpDirRecurse"
67+
"NoXpDirRecurse",
68+
"StopAtLsn"
6869
)
6970
Compare-Object -ReferenceObject $expectedParameters -DifferenceObject $hasParameters | Should -BeNullOrEmpty
7071
}
@@ -901,7 +902,7 @@ use master
901902
}
902903

903904

904-
Context -Skip "Test restoring with StopAt and StopAfterDate" {
905+
Context -Skip "Test restoring with StopAt, StopAtLsn and StopAfterDate" {
905906
BeforeAll {
906907
$null = Get-DbaDatabase -SqlInstance $TestConfig.InstanceSingle -ExcludeSystem -EnableException | Remove-DbaDatabase -EnableException
907908
}
@@ -913,6 +914,21 @@ use master
913914
$sqlOut.ms | Should -Be 29876
914915
$null = Remove-DbaDatabase -SqlInstance $TestConfig.InstanceSingle -Database StopAt2
915916
}
917+
918+
It "Should have stoped at lsn" {
919+
$dbName = "TestStopAtLsn_$(Get-Random)"
920+
$null = New-DbaDatabase -SqlInstance $TestConfig.InstanceSingle -Name $dbName
921+
$fullBackup = Backup-DbaDatabase -SqlInstance $TestConfig.InstanceSingle -Database $dbName -Path $backupPath
922+
Invoke-DbaQuery -SqlInstance $TestConfig.InstanceSingle -Database $dbName -Query "CREATE TABLE Test (id int IDENTITY)"
923+
1..5 | ForEach-Object -Process { Invoke-DbaQuery -SqlInstance $TestConfig.InstanceSingle -Database $dbName -Query "INSERT INTO Test DEFAULT VALUES" }
924+
$lsn = Invoke-DbaQuery -SqlInstance $TestConfig.InstanceSingle -Database $dbName -Query "SELECT MAX([Current LSN]) FROM sys.fn_dblog(NULL, NULL)" -As SingleValue
925+
1..5 | ForEach-Object -Process { Invoke-DbaQuery -SqlInstance $TestConfig.InstanceSingle -Database $dbName -Query "INSERT INTO Test DEFAULT VALUES" }
926+
$logBackup = Backup-DbaDatabase -SqlInstance $TestConfig.InstanceSingle -Database $dbName -Path $backupPath -Type Log
927+
$null = Restore-DbaDatabase -SqlInstance $TestConfig.InstanceSingle -Path $fullBackup.Path, $logBackup.Path -DatabaseName $dbName -StopAtLsn "0x$lsn" -WithReplace
928+
$id = Invoke-DbaQuery -SqlInstance $TestConfig.InstanceSingle -Database $dbName -Query "SELECT MAX(id) FROM Test" -As SingleValue
929+
$id | Should -Be 5
930+
$null = Remove-DbaDatabase -SqlInstance $TestConfig.InstanceSingle -Database $dbName
931+
}
916932
}
917933

918934

0 commit comments

Comments
 (0)