예제 #1
0
        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));
        }