/// <summary> /// Create a new <see cref="BackupMetadataFile"/> and write it to the given file. /// </summary> /// <returns>Task that represents the asynchronous operation.</returns> public static async Task <BackupMetadataFile> CreateAsync( string fileName, BackupOption backupOption, Guid parentBackupId, Guid backupId, Guid partitionId, long replicaId, Epoch startingEpoch, long startingLsn, Epoch backupEpoch, long backupLsn, CancellationToken cancellationToken) { // Create the file with asynchronous flag and 4096 cache size (C# default). // MCoskun: Default IoPriorityHint is used. // Reason: Backup is a user operation. using (var filestream = FabricFile.Open( fileName, FileMode.CreateNew, FileAccess.Write, FileShare.Read, 4096, FileOptions.Asynchronous)) { Utility.SetIoPriorityHint(filestream.SafeFileHandle, Kernel32Types.PRIORITY_HINT.IoPriorityHintLow); var backupMetadataFile = new BackupMetadataFile(fileName); backupMetadataFile.properties = new BackupMetadataFileProperties() { BackupOption = backupOption, ParentBackupId = parentBackupId, BackupId = backupId, PartitionId = partitionId, ReplicaId = replicaId, StartingEpoch = startingEpoch, StartingLsn = startingLsn, BackupEpoch = backupEpoch, BackupLsn = backupLsn, }; // Write the properties. var propertiesHandle = await FileBlock.WriteBlockAsync(filestream, writer => backupMetadataFile.properties.Write(writer)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); // Write the footer. backupMetadataFile.footer = new FileFooter(propertiesHandle, Version); await FileBlock.WriteBlockAsync(filestream, writer => backupMetadataFile.footer.Write(writer)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); // Flush to underlying stream. await filestream.FlushAsync(cancellationToken).ConfigureAwait(false); return(backupMetadataFile); } }
/// <summary> /// Read the list of state providers' metadata from the <see cref="StateManagerFile"/>. /// </summary> /// <param name="stream">Stream to read from.</param> /// <param name="traceType">Tracing type information.</param> /// <param name="deserializeTypes">Should the types be deserialized.</param> /// <param name="cancellationToken">Token used to signal cancellation.</param> /// <returns>The list of state providers' metadata read.</returns> private async Task <List <SerializableMetadata> > ReadMetadataAsync( Stream stream, string traceType, bool deserializeTypes, CancellationToken cancellationToken) { var metadataList = new List <SerializableMetadata>((int)this.properties.StateProviderCount); // Read the blocks, to improve sequential reads. var blocks = await FileBlock.ReadBlockAsync( stream, this.properties.BlocksHandle, (sectionReader, sectionHandle) => StateManagerFileBlocks.Read(sectionReader, sectionHandle)).ConfigureAwait(false); // Currently, we expect the block sizes to always be present. if (blocks == null || blocks.RecordBlockSizes == null) { throw new InvalidDataException(string.Format(CultureInfo.CurrentCulture, SR.Error_SMFile_Corrupt_BlockSizesMissing)); } var recordBlockSizes = blocks.RecordBlockSizes; // Read blocks from the file. Each state provider metadata is checksummed individually. using (var itemStream = new MemoryStream(DesiredBlockSize * 2)) using (var itemReader = new InMemoryBinaryReader(itemStream)) { stream.Position = this.properties.MetadataHandle.Offset; var endOffset = this.properties.MetadataHandle.EndOffset; // Each block has one or more state provider metadata records. foreach (var recordBlockSize in recordBlockSizes) { if (stream.Position + recordBlockSize > endOffset) { throw new InvalidDataException( string.Format(CultureInfo.CurrentCulture, SR.Error_SMFile_Corrupt_BlockExtendPastFile)); } // Read the block into memory. itemStream.Position = 0; itemStream.SetLength(recordBlockSize); await stream.ReadAsync(itemStream.GetBuffer(), 0, recordBlockSize, cancellationToken).ConfigureAwait(false); // Read to the end of the metadata section. var endBlockOffset = itemStream.Length; while (itemStream.Position < endBlockOffset) { var position = itemStream.Position; // Read the record size and validate it is not obviously corrupted. if (position + sizeof(int) > endBlockOffset) { throw new InvalidDataException( string.Format(CultureInfo.CurrentCulture, SR.Error_SMFile_Corrupt_MissingRecordSize)); } var recordSize = itemReader.ReadInt32(); var recordSizeWithChecksum = recordSize + sizeof(ulong); // We need to do extra validation on the recordSize, because we haven't validated the bits // against the checksum and we need the recordSize to locate the checksum. if (recordSize < 0) { throw new InvalidDataException( string.Format(CultureInfo.CurrentCulture, SR.Error_SMFile_Corrupt_NegativeRecordSize)); } if (position + recordSize > endBlockOffset) { throw new InvalidDataException( string.Format(CultureInfo.CurrentCulture, SR.Error_SMFile_Corrupt_RecordExtendsPastFile)); } if (position + recordSizeWithChecksum > endBlockOffset) { throw new InvalidDataException( string.Format(CultureInfo.CurrentCulture, SR.Error_SMFile_Corrupt_MissingChecksum)); } // Compute the checksum. var computedChecksum = CRC64.ToCRC64( itemStream.GetBuffer(), checked ((int)position), recordSize); // Read the checksum (checksum is after the record bytes). itemStream.Position = position + recordSize; var checksum = itemReader.ReadUInt64(); // Verify the checksum. if (checksum != computedChecksum) { throw new InvalidDataException( string.Format(CultureInfo.CurrentCulture, SR.Error_SMFile_Corrupt_MismatchedChecksum)); } // Read and re-create the state provider metadata, now that the checksum is validated. itemStream.Position = position; var metadata = ReadMetadata(itemReader, traceType, deserializeTypes); metadataList.Add(metadata); itemStream.Position = position + recordSizeWithChecksum; } } } // Post-validation. if (metadataList.Count != (int)this.Properties.StateProviderCount) { throw new InvalidDataException( string.Format(CultureInfo.CurrentCulture, SR.Error_SMFile_Corrupt_MetadataCountMismatch)); } return(metadataList); }
/// <summary> /// Open an existing <see cref="StateManagerFile"/> from the given file path. /// </summary> /// <param name="filePath">Path to the checkpoint file.</param> /// <param name="traceType">Tracing information.</param> /// <param name="deserializeTypes">Should the type information be deserialized.</param> /// <param name="cancellationToken">Token used to signal cancellation.</param> /// <returns>The opened <see cref="StateManagerFile"/>.</returns> internal static async Task <StateManagerFile> OpenAsync( string filePath, string traceType, bool deserializeTypes, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(filePath)) { throw new ArgumentException(SR.Error_FilePath_Null_NoArgs); } if (!FabricFile.Exists(filePath)) { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.Error_FilePath_Null, filePath)); } // Open the file with asynchronous flag and 4096 cache size (C# default). // MCoskun: Default IoPriorityHint is used. // Reason: Used during open, restore and complete checkpoint. using (var filestream = FabricFile.Open( filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.Asynchronous)) { var stateManagerFile = new StateManagerFile(filePath); // Read and validate the Footer section. The footer is always at the end of the stream, minus space for the checksum. var footerHandle = new BlockHandle( filestream.Length - FileFooter.SerializedSize - sizeof(ulong), FileFooter.SerializedSize); stateManagerFile.footer = await FileBlock.ReadBlockAsync( filestream, footerHandle, (reader, handle) => FileFooter.Read(reader, handle)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); // Verify we know how to deserialize this version of the state manager checkpoint file. if (stateManagerFile.footer.Version != Version) { throw new InvalidDataException( string.Format( CultureInfo.CurrentCulture, "StateManager checkpoint file cannot be deserialized (unknown version number: {0}, expected version number: {1})", stateManagerFile.footer.Version, Version)); } // Read and validate the properties section. var propertiesHandle = stateManagerFile.footer.PropertiesHandle; stateManagerFile.properties = await FileBlock.ReadBlockAsync( filestream, propertiesHandle, (reader, handle) => FileProperties.Read <StateManagerFileProperties>(reader, handle)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); // Read the state provider metadata. stateManagerFile.StateProviderMetadata = await stateManagerFile.ReadMetadataAsync(filestream, traceType, deserializeTypes, cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); stateManagerFile.FileSize = filestream.Length; return(stateManagerFile); } }
/// <summary> /// Create a new <see cref="StateManagerFile"/> and write it to the given file. /// </summary> /// <param name="filePath">Path to the checkpoint file.</param> /// <param name="partitionId">Service partition Id.</param> /// <param name="replicaId">Service replica Id.</param> /// <param name="metadataList">The state providers' metadata.</param> /// <param name="allowPrepareCheckpointLSNToBeInvalid"> /// Allow the prepare checkpoint LSN to be invalid. If it is invalid, do not write it. /// </param> /// <param name="prepareCheckpointLSN">The PrepareCheckpointLSN.</param> /// <param name="cancellationToken">Token used to signal cancellation.</param> /// <returns>The new <see cref="BackupLogFile"/>.</returns> public static async Task <StateManagerFile> CreateAsync( string filePath, Guid partitionId, long replicaId, List <SerializableMetadata> metadataList, bool allowPrepareCheckpointLSNToBeInvalid, long prepareCheckpointLSN, CancellationToken cancellationToken) { // #12249219: Without the "allowPrepareCheckpointLSNToBeInvalid" being set to true in the backup code path, // It is possible for all backups before the first checkpoint after the upgrade to fail. // If the code has the PrepareCheckpointLSN property, it must be larger than or equal to ZeroLSN(0). Utility.Assert( allowPrepareCheckpointLSNToBeInvalid || prepareCheckpointLSN >= StateManagerConstants.ZeroLSN, "{0}:{1} CreateAsync: In the write path, prepareCheckpointLSN must be larger or equal to 0. PrepareCheckpointLSN: {2}.", partitionId, replicaId, prepareCheckpointLSN); // Create the file with asynchronous flag and 4096 cache size (C# default). // MCoskun: Default IoPriorityHint is used. // Reason: Used during backup and checkpoint (user operation and life-cycle operation respectively). using (var filestream = FabricFile.Open( filePath, FileMode.Create, FileAccess.Write, FileShare.Write, 4096, FileOptions.Asynchronous)) { var stateManagerFile = new StateManagerFile(filePath); stateManagerFile.StateProviderMetadata = metadataList; bool shouldNotWritePrepareCheckpointLSN = prepareCheckpointLSN == StateManagerConstants.InvalidLSN ? true : false; stateManagerFile.properties = new StateManagerFileProperties(shouldNotWritePrepareCheckpointLSN); stateManagerFile.properties.PartitionId = partitionId; stateManagerFile.properties.ReplicaId = replicaId; if (shouldNotWritePrepareCheckpointLSN == false) { stateManagerFile.properties.PrepareCheckpointLSN = prepareCheckpointLSN; } // Write the state provider metadata. var blocks = await stateManagerFile.WriteMetadataAsync(filestream, metadataList, cancellationToken).ConfigureAwait(false); stateManagerFile.properties.MetadataHandle = new BlockHandle(offset: 0, size: filestream.Position); cancellationToken.ThrowIfCancellationRequested(); // Write the blocks and update the properties. stateManagerFile.properties.BlocksHandle = await FileBlock.WriteBlockAsync(filestream, (sectionWriter) => blocks.Write(sectionWriter)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); // Write the properties. var propertiesHandle = await FileBlock.WriteBlockAsync(filestream, writer => stateManagerFile.properties.Write(writer)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); // Write the footer. stateManagerFile.footer = new FileFooter(propertiesHandle, Version); await FileBlock.WriteBlockAsync(filestream, writer => stateManagerFile.footer.Write(writer)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); // Ensure we fully flush the data to disk before returning. await filestream.FlushAsync().ConfigureAwait(false); stateManagerFile.FileSize = filestream.Length; return(stateManagerFile); } }
/// <summary> /// Open an existing <see cref="BackupLogFile"/> from the given file path. /// </summary> /// <param name="fileName">Path to the backup log file.</param> /// <param name="cancellationToken">Token used to signal cancellation.</param> /// <returns>The opened <see cref="BackupLogFile"/>.</returns> public static async Task <BackupLogFile> OpenAsync(string fileName, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(fileName)) { throw new ArgumentException(SR.Error_FilePath_Null_NoArgs); } if (!FabricFile.Exists(fileName)) { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.Error_FilePath_Null, fileName)); } // Open the file with asynchronous flag and 4096 cache size (C# default). // MCoskun: Uses default IOPriorityHint since this operation is called during Restore. using (var filestream = FabricFile.Open( fileName, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.Asynchronous)) { var backupLogFile = new BackupLogFile(fileName); backupLogFile.Size = filestream.Length; // Read and validate the Footer section. The footer is always at the end of the stream, minus space for the checksum. var footerHandle = new BlockHandle( filestream.Length - FileFooter.SerializedSize - sizeof(ulong), FileFooter.SerializedSize); backupLogFile.footer = await FileBlock.ReadBlockAsync( filestream, footerHandle, (reader, handle) => FileFooter.Read(reader, handle)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); // Verify we know how to deserialize this version of the backup log file. if (backupLogFile.footer.Version != Version) { throw new InvalidDataException( string.Format( CultureInfo.CurrentCulture, SR.Error_BackupLogFile_Deserialized, backupLogFile.footer.Version, Version)); } // Read and validate the properties section. var propertiesHandle = backupLogFile.footer.PropertiesHandle; backupLogFile.properties = await FileBlock.ReadBlockAsync( filestream, propertiesHandle, (reader, handle) => FileProperties.Read <BackupLogFileProperties>(reader, handle)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); return(backupLogFile); } }
/// <summary> /// Create a new <see cref="BackupLogFile"/> and write it to the given file. /// </summary> /// <param name="fileName">Name of the backup file.</param> /// <param name="logRecords">The log records.</param> /// <param name="lastBackupLogRecord"></param> /// <param name="cancellationToken">Token used to signal cancellation.</param> /// <returns>The new <see cref="BackupLogFile"/>.</returns> public static async Task <BackupLogFile> CreateAsync( string fileName, IAsyncEnumerator <LogRecord> logRecords, BackupLogRecord lastBackupLogRecord, CancellationToken cancellationToken) { var stopwatch = new Stopwatch(); stopwatch.Start(); var backupLogFile = new BackupLogFile(fileName); backupLogFile.properties = new BackupLogFileProperties(); // Create the file with asynchronous flag and 4096 cache size (C# default). using (var filestream = FabricFile.Open( fileName, FileMode.CreateNew, FileAccess.Write, FileShare.Write, 4096, FileOptions.Asynchronous)) { Utility.SetIoPriorityHint(filestream.SafeFileHandle, Kernel32Types.PRIORITY_HINT.IoPriorityHintLow); var incrementalBackupRecords = logRecords as IncrementalBackupLogRecordAsyncEnumerator; if (incrementalBackupRecords == null) { await backupLogFile.WriteLogRecordsAsync(filestream, logRecords, cancellationToken).ConfigureAwait(false); } else { await backupLogFile.WriteLogRecordsAsync(filestream, incrementalBackupRecords, cancellationToken).ConfigureAwait(false); await incrementalBackupRecords.VerifyDrainedAsync().ConfigureAwait(false); Utility.Assert(backupLogFile.Count == incrementalBackupRecords.Count, "Unexpected count"); backupLogFile.properties.IndexingRecordEpoch = incrementalBackupRecords.StartingEpoch; backupLogFile.properties.IndexingRecordLsn = incrementalBackupRecords.StartingLsn; if (incrementalBackupRecords.HighestBackedupEpoch == LoggingReplicator.InvalidEpoch) { backupLogFile.properties.LastBackedUpEpoch = lastBackupLogRecord.HighestBackedUpEpoch; } else { backupLogFile.properties.LastBackedUpEpoch = incrementalBackupRecords.HighestBackedupEpoch; } backupLogFile.properties.LastBackedUpLsn = incrementalBackupRecords.HighestBackedupLsn; } // Write the log records. backupLogFile.properties.RecordsHandle = new BlockHandle(offset: 0, size: filestream.Position); cancellationToken.ThrowIfCancellationRequested(); // Write the properties. var propertiesHandle = await FileBlock.WriteBlockAsync(filestream, writer => backupLogFile.properties.Write(writer)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); // Write the footer. backupLogFile.footer = new FileFooter(propertiesHandle, Version); await FileBlock.WriteBlockAsync(filestream, writer => backupLogFile.footer.Write(writer)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); // Store the size. backupLogFile.Size = filestream.Length; } stopwatch.Stop(); backupLogFile.WriteTimeInMilliseconds = stopwatch.ElapsedMilliseconds; return(backupLogFile); }