/// <summary> /// Creates or finds the log stream. /// If being created either initializes the log with default log records or records from backup log. /// </summary> /// <param name="openMode">Open mode of the replica.</param> /// <returns>Task that represents the asynchronous open operation.</returns> internal async Task <PhysicalLogReader> OpenAsync(ReplicaOpenMode openMode) { // TODO: Anurag: do we plumb c.token up? this.LogicalLog = await this.CreateLogFileAsync(openMode == ReplicaOpenMode.New, CancellationToken.None).ConfigureAwait(false); var logLogLength = this.LogicalLog.Length; if (logLogLength <= sizeof(int)) { // No usable content in the log if (this.LogicalLog.WritePosition > 0) { await this.LogicalLog.TruncateTail(0, CancellationToken.None).ConfigureAwait(false); // Remove all contents and reset write cursor back to 0 Utility.Assert(this.LogicalLog.Length == 0, "this.logicalLog.Length == 0"); Utility.Assert(this.LogicalLog.WritePosition == 0, "this.logicalLog.WritePosition == 0"); } using ( var logWriter = new PhysicalLogWriter( this.LogicalLog, this.emptyCallbackManager, this.Tracer, this.MaxWriteCacheSizeInMB, this.IncomingBytesRateCounterWriter, this.LogFlushBytesRateCounterWriter, this.BytesPerFlushCounterWriter, this.AvgFlushLatencyCounterWriter, this.AvgSerializationLatencyCounterWriter, false)) { var zeroIndexRecord = IndexingLogRecord.CreateZeroIndexingLogRecord(); logWriter.InsertBufferedRecord(zeroIndexRecord); logWriter.InsertBufferedRecord(UpdateEpochLogRecord.CreateZeroUpdateEpochLogRecord()); var zeroBeginCheckpointRecord = BeginCheckpointLogRecord.CreateZeroBeginCheckpointLogRecord(); logWriter.InsertBufferedRecord(zeroBeginCheckpointRecord); logWriter.InsertBufferedRecord(BarrierLogRecord.CreateOneBarrierLogRecord()); var oneEndCheckpointRecord = EndCheckpointLogRecord.CreateOneEndCheckpointLogRecord( zeroBeginCheckpointRecord, zeroIndexRecord); logWriter.InsertBufferedRecord(oneEndCheckpointRecord); var endCompleteCheckpointRecord = new CompleteCheckpointLogRecord( LogicalSequenceNumber.OneLsn, zeroIndexRecord, oneEndCheckpointRecord); logWriter.InsertBufferedRecord(endCompleteCheckpointRecord); await logWriter.FlushAsync("OpenAsync").ConfigureAwait(false); // This additional await is required to ensure the log record was indeed flushed. // Without this, the flushasync could succeed, but the log record flush could have failed due to a write error await endCompleteCheckpointRecord.AwaitFlush().ConfigureAwait(false); } } return(new PhysicalLogReader(this)); }
internal async Task <PhysicalLogReader> OpenWithRestoreFileAsync( ReplicaOpenMode openMode, FabricPerformanceCounterSetInstance perfCounterInstance, IList <string> backupLogFilePathList, int flushEveryNKB) { this.LogicalLog = await this.CreateLogFileAsync(openMode == ReplicaOpenMode.New, CancellationToken.None).ConfigureAwait(false); // No usable content in the log if (this.LogicalLog.WritePosition > 0) { await this.LogicalLog.TruncateTail(0, CancellationToken.None).ConfigureAwait(false); // Remove all contents and reset write cursor back to 0 Utility.Assert( this.LogicalLog.Length == 0 && this.LogicalLog.WritePosition == 0, "{0}: this.logicalLog.Length: {1} this.logicalLog.WritePosition: {2}", this.tracer.Type, this.LogicalLog.Length, this.LogicalLog.WritePosition); } using (PhysicalLogWriter logWriter = new PhysicalLogWriter( this.LogicalLog, this.emptyCallbackManager, this.Tracer, this.MaxWriteCacheSizeInMB, this.IncomingBytesRateCounterWriter, this.LogFlushBytesRateCounterWriter, this.BytesPerFlushCounterWriter, this.AvgFlushLatencyCounterWriter, this.AvgSerializationLatencyCounterWriter, true)) { LogRecord record = null; LogRecordsMap logRecordsMap = null; long bufferedRecordsSizeBytes = -1; long backupRecordIndex = 0; foreach (string backupLogFilePath in backupLogFilePathList) { BackupLogFile backupLogFile = await BackupLogFile.OpenAsync( backupLogFilePath, CancellationToken.None).ConfigureAwait(false); using (var backupLogEnumerator = backupLogFile.GetAsyncEnumerator()) { if (logRecordsMap == null) { bool hasFirstRecord = await backupLogEnumerator.MoveNextAsync(CancellationToken.None).ConfigureAwait(false); Utility.Assert(hasFirstRecord, "{0}: Backup must include at least six records.", this.tracer.Type); // If the log is being restored. // First record must be a indexing log record. Flush it. LogRecord firstRecordFromBackupLog = backupLogEnumerator.Current; Utility.Assert( null != firstRecordFromBackupLog, "{0}: BackupLogEnumerator will never return null", this.tracer.Type); Utility.Assert( false == LogRecord.IsInvalidRecord(firstRecordFromBackupLog), "{0}: First record read from the backup log cannot be invalid", this.tracer.Type); IndexingLogRecord logHead = firstRecordFromBackupLog as IndexingLogRecord; Utility.Assert( null != logHead, "{0}: First record read from the backup log must be indexing log record: Type: {1} LSN: {2} PSN: {3} Position: {4}", this.tracer.Type, firstRecordFromBackupLog.RecordType, firstRecordFromBackupLog.Lsn.LSN, firstRecordFromBackupLog.Psn.PSN, firstRecordFromBackupLog.RecordPosition); logRecordsMap = new LogRecordsMap(logHead, this.Tracer); logRecordsMap.ProcessLogRecord(logHead); bufferedRecordsSizeBytes = logWriter.InsertBufferedRecord(logHead); // Note that logHead.PreviousPhysicalRecord is an InvalidLogRecord. backupRecordIndex++; FabricEvents.Events.RestoreOperationAsync( this.Tracer.Type, logHead.RecordType.ToString(), backupRecordIndex, logHead.Lsn.LSN, logHead.Psn.PSN, long.MaxValue); } while (await backupLogEnumerator.MoveNextAsync(CancellationToken.None).ConfigureAwait(false)) { record = backupLogEnumerator.Current; logRecordsMap.ProcessLogRecord(record); // Note: Function inserts a record to the buffer where it waits to be flushed and return // the new size of the whole buffer. bufferedRecordsSizeBytes = logWriter.InsertBufferedRecord(record); backupRecordIndex++; FabricEvents.Events.RestoreOperationAsync( this.Tracer.Type, record.RecordType.ToString(), backupRecordIndex, record.Lsn.LSN, record.Psn.PSN, record.PreviousPhysicalRecord.Psn.PSN); // TODO: Use a backup config for this flush size determination if (bufferedRecordsSizeBytes > flushEveryNKB * 1024) { string intermediateFlushMessage = string.Format( CultureInfo.InvariantCulture, "LogManager: OpenWithRestoreFile (Restore) Intermediate Flush Size: {0} bytes, Index: {1}", bufferedRecordsSizeBytes, backupRecordIndex); await logWriter.FlushAsync(intermediateFlushMessage).ConfigureAwait(false); // This additional await is required to ensure the log record was indeed flushed. // Without this, the flushasync could succeed, but the log record flush could have failed due to a write error await record.AwaitFlush().ConfigureAwait(false); bufferedRecordsSizeBytes = 0; } } } } // Note: Move the last flush for remaining items out of the loop to avoid unnecessary flush in the // case of each iteration has a small number of un-flushed log records. if (bufferedRecordsSizeBytes > 0) { string flushMessage = string.Format( CultureInfo.InvariantCulture, "LogManager: OpenWithRestoreFile (Restore)"); await logWriter.FlushAsync(flushMessage).ConfigureAwait(false); // This additional await is required to ensure the log record was indeed flushed. // Without this, the flushasync could succeed, but the log record flush could have failed due to a write error await record.AwaitFlush().ConfigureAwait(false); } } return(new PhysicalLogReader(this)); }