azurecopydatabase-restoreazure-managed-database

How to copy managed database?


AFAIK there is no REST API providing this functionality directly. So, I am using restore for this (there are other ways but those don’t guarantee transactional consistency and are more complicated) via Create request.

Since it is not possible to turn off short time backup (retention has to be at least 1 day) it should be reliable. I am using current time for ‘properties.restorePointInTime’ property in request. This works fine for most databases. But one db returns me this error (from async operation request):

"error": {
    "code": "BackupSetNotFound",
    "message": "No backups were found to restore the database to the point in time 6/14/2021 8:20:00 PM (UTC). Please contact support to restore the database."
}

I know I am not out of range because if the restore time is before ‘earliestRestorePoint’ (this can be found in GET request on managed database) or in future I get ‘PitrPointInTimeInvalid’ error. Nevertheless, I found some information that I shouldn’t use current time but rather current time - 6 minutes at most. This is also true if done via Azure Portal (where it fails with the same error btw) which doesn’t allow to input time newer than current - 6 minutes. After few tries, I found out that current time - circa 40 minutes starts to work fine. But 40 minutes is a lot and I didn’t find any way to find out what time works before I try and wait for result of async operation.

My question is: Is there a way to find what is the latest time possible for restore?

Or is there a better way to do ‘copy’ of managed database which guarantees transactional consistency and is reasonably quick?

EDIT:
The issue I was describing was reported to MS. It was occuring when:

  1. there is a custom time zone format e.g. UTC + 1 hour.
  2. Backups are skipped for the source database at the desired point in time because the database is inactive (no active transactions).

This should be fixed as of now (25th of August 2021) and I were not able to reproduce it with current time - 10 minutes. Also I was told there should be new API which would allow to make copy without using PITR (no sooner than 1Q/22).


Solution

  • To answer your first question "Is there a way to find what is the latest time possible for restore?"

    Yes. Via SQL. The only way to find this out is by using extended event (XEvent) sessions to monitor backup activity.

    Process to start logging the backup_restore_progress_trace extended event and report on it is described here https://learn.microsoft.com/en-us/azure/azure-sql/managed-instance/backup-activity-monitor

    Including the SQL here in case the link goes stale.

    This is for storing in the ring buffer (max last 1000 records):

    CREATE EVENT SESSION [Verbose backup trace] ON SERVER 
    ADD EVENT sqlserver.backup_restore_progress_trace(
        WHERE (
                  [operation_type]=(0) AND (
                  [trace_message] like '%100 percent%' OR 
                  [trace_message] like '%BACKUP DATABASE%' OR [trace_message] like '%BACKUP LOG%'))
           )
    ADD TARGET package0.ring_buffer
    WITH (MAX_MEMORY=4096 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,
           MAX_DISPATCH_LATENCY=30 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=NONE,
           TRACK_CAUSALITY=OFF,STARTUP_STATE=ON)
    
    ALTER EVENT SESSION [Verbose backup trace] ON SERVER
    STATE = start;
    

    Then to see output of all backup events:

    WITH
    a AS (SELECT xed = CAST(xet.target_data AS xml)
    FROM sys.dm_xe_session_targets AS xet
    JOIN sys.dm_xe_sessions AS xe
    ON (xe.address = xet.event_session_address)
    WHERE xe.name = 'Verbose backup trace'),
    b AS(SELECT
    d.n.value('(@timestamp)[1]', 'datetime2') AS [timestamp],
    ISNULL(db.name, d.n.value('(data[@name="database_name"]/value)[1]', 'varchar(200)')) AS database_name,
    d.n.value('(data[@name="trace_message"]/value)[1]', 'varchar(4000)') AS trace_message
    FROM a
    CROSS APPLY  xed.nodes('/RingBufferTarget/event') d(n)
    LEFT JOIN master.sys.databases db
    ON db.physical_database_name = d.n.value('(data[@name="database_name"]/value)[1]', 'varchar(200)'))
    SELECT * FROM b
    

    NOTE: This tip came to me via Microsoft support when I had the same issue of point in time restores failing what seemed like randomly. They do not give any SLA for log backups. I found that on a busy database the log backups seemed to happen every 5-10 minutes but on a quiet database hourly. Recovery of a database this way can be slow depending on number of transaction logs and amount of activity to replay etc. (https://learn.microsoft.com/en-us/azure/azure-sql/database/recovery-using-backups)

    To answer your second question: "Or is there a better way to do ‘copy’ of managed database which guarantees transactional consistency and is reasonably quick?"

    I'd have to agree with Thomas - if you're after guaranteed transactional consistency and speed you need to look at creating a failover group https://learn.microsoft.com/en-us/azure/azure-sql/database/auto-failover-group-overview?tabs=azure-powershell#best-practices-for-sql-managed-instance and https://learn.microsoft.com/en-us/azure/azure-sql/managed-instance/failover-group-add-instance-tutorial?tabs=azure-portal

    A failover group for a managed instance will have a primary server and failover server with the same user databases on each kept in synch.

    But yes, whether this suits your needs depends on the question Thomas asked of what is the purpose of the copy.