/// <summary>
        /// Asynchronously starts the creation of a backup of the state of this replica and stores that into the central store.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="backupOption"></param>
        /// <returns></returns>
        public static async Task BeginCreateBackup(this IBackupRestoreServiceInternal service, BackupOption backupOption)
        {
            var backupDescription = new BackupDescription(backupOption, service.PostBackupCallbackAsync);
            await service.BackupAsync(backupDescription);

            service.LogCallback?.Invoke($"BackupRestoreService - BeginCreateBackup for partition {service.Context.PartitionId}.");
        }
        /// <summary>
        /// Called after the call to <see cref="StatefulServiceBase.BackupAsync(Microsoft.ServiceFabric.Data.BackupDescription)"/> has completed. Used
        /// to save a copy of that backup in the central store.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="backupInfo"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static async Task <bool> PostBackupCallbackAsync(this IBackupRestoreServiceInternal service, BackupInfo backupInfo, CancellationToken cancellationToken)
        {
            service.LogCallback?.Invoke($"BackupRestoreService - performing PostBackupCallbackAsync'.");
            var metadata = await service.CentralBackupStore.UploadBackupFolderAsync(backupInfo.Option, service.Context.PartitionId,
                                                                                    backupInfo.Directory, cancellationToken);

            service.LogCallback?.Invoke($"BackupRestoreService - performed PostBackupCallbackAsync backupID:'{metadata.BackupId}'.");

            return(true);
        }
        /// <summary>
        /// Lists all centrally stored backups present in <paramref name="service"/> CentralBackupStore.
        /// </summary>
        /// <param name="service"></param>
        /// <returns></returns>
        public static async Task <IEnumerable <BackupMetadata> > ListAllBackups(this IBackupRestoreServiceInternal service)
        {
            service.LogCallback?.Invoke($"BackupRestoreService - Listing backups");
            var backups = (await service.CentralBackupStore.GetBackupMetadataAsync())
                          .OrderByDescending(x => x.TimeStampUtc)
                          .ToList();

            service.LogCallback?.Invoke($"BackupRestoreService - Returning {backups.Count} backups");

            return(backups);
        }
        /// <summary>
        /// Compiles a list of <see cref="BackupMetadata"/> to be restored, based on the provided metadata.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="metadata"></param>
        /// <returns></returns>
        internal static async Task <List <BackupMetadata> > GetBackupMetadataAsync(IBackupRestoreServiceInternal service, BackupMetadata metadata)
        {
            var backupList = new List <BackupMetadata>();

            if (metadata.BackupOption == BackupOption.Full)
            {
                // Taking only selected full backup
                backupList.Add(metadata);
            }
            else
            {
                // Taking full backup and all incremental backups since, reversed
                // so it finds the incremental backup first, and then loops until the full backup is found.
                var allBackups = (await service.CentralBackupStore.GetBackupMetadataAsync(servicePartitionId: metadata.OriginalServicePartitionId))
                                 .OrderByDescending(x => x.TimeStampUtc)
                                 .ToList();

                BackupMetadata incrementalBackup = null;
                // Looking for the latest full backup before selected
                foreach (var backupMetadata in allBackups)
                {
                    if (incrementalBackup == null && backupMetadata.BackupId != metadata.BackupId)
                    {
                        continue;
                    }
                    else
                    {
                        incrementalBackup = backupMetadata;
                        backupList.Add(backupMetadata);
                    }

                    if (backupMetadata.BackupOption == BackupOption.Full)
                    {
                        //if it's the full backup we encounter, we're done
                        break;
                    }
                }
                backupList.Reverse();
            }

            return(backupList);
        }
        /// <summary>
        /// Asynchronously starts a restore operation using the state indicated by <paramref name="backupMetadata"/>.
        /// The backup is retrieved from the central store.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="dataLossMode"></param>
        /// <param name="backupMetadata"></param>
        /// <returns></returns>
        public static async Task BeginRestoreBackup(this IBackupRestoreServiceInternal service, BackupMetadata backupMetadata, DataLossMode dataLossMode)
        {
            service.LogCallback?.Invoke($"BackupRestoreService - Beginning restore backup {backupMetadata.BackupId} for partition {service.Context.PartitionId}.");

            if (backupMetadata == null)
            {
                throw new ArgumentNullException(nameof(backupMetadata));
            }

            await service.CentralBackupStore.ScheduleBackupAsync(service.Context.PartitionId, backupMetadata.BackupId);

            var partitionSelector = PartitionSelector.PartitionIdOf(service.Context.ServiceName, service.Context.PartitionId);

            var operationId = Guid.NewGuid();

            await new FabricClient(FabricClientRole.Admin).TestManager.StartPartitionDataLossAsync(operationId, partitionSelector, dataLossMode);
            //Causes OnDataLossAsync to be called.

            service.LogCallback?.Invoke($"BackupRestoreService - Begun restore backup {backupMetadata.BackupId} for partition {service.Context.PartitionId}.");
        }
        /// <summary>
        /// This method is called during suspected data loss.
        /// You can use this method to restore the service in case of data loss.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="restoreCtx"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static async Task <bool> OnDataLossAsync(this IBackupRestoreServiceInternal service, RestoreContext restoreCtx, CancellationToken cancellationToken)
        {
            //caused by BeginRestoreBackup
            service.LogCallback?.Invoke($"BackupRestoreService - OnDataLossAsync starting for partition: {service.Context.PartitionId}.");

            var metadata = await service.CentralBackupStore.RetrieveScheduledBackupAsync(service.Context.PartitionId);

            if (metadata == null)
            {
                return(false);
            }

            List <BackupMetadata> backupList = await GetBackupMetadataAsync(service, metadata);

            if (backupList.Count == 0)
            {
                throw new InvalidOperationException("Failed to find any backups for this partition.");
            }
            if (backupList[0].BackupOption != BackupOption.Full)
            {
                throw new InvalidOperationException("Failed to find any full backups for this partition.");
            }

            string localBackupFolder = Path.Combine(service.Context.CodePackageActivationContext.WorkDirectory, Guid.NewGuid().ToString("N"));

            foreach (var backupMetadata in backupList)
            {
                string subFolder = Path.Combine(localBackupFolder, backupMetadata.TimeStampUtc.ToString("yyyyMMddhhmmss"));
                await service.CentralBackupStore.DownloadBackupFolderAsync(backupMetadata.BackupId, subFolder, cancellationToken);
            }
            var restoreDescription = new RestoreDescription(localBackupFolder, RestorePolicy.Force);
            await restoreCtx.RestoreAsync(restoreDescription, cancellationToken);

            Directory.Delete(localBackupFolder, true);
            service.LogCallback?.Invoke($"BackupRestoreService - OnDataLossAsync complete for partition {service.Context.PartitionId}.");

            return(true);
        }