// Derived class exposed API internal void RecoverFuzzyIndex(IndexCheckpointInfo info) { var token = info.info.token; var ht_version = resizeInfo.version; Debug.Assert(state[ht_version].size == info.info.table_size); // Create devices to read from using Async API info.main_ht_device = Devices.CreateLogDevice(directoryConfiguration.GetPrimaryHashTableFileName(token), false); info.ofb_device = Devices.CreateLogDevice(directoryConfiguration.GetOverflowBucketsFileName(token), false); BeginMainIndexRecovery(ht_version, info.main_ht_device, info.info.num_ht_bytes); overflowBucketsAllocator.Recover( info.ofb_device, info.info.num_buckets, info.info.num_ofb_bytes); // Wait until reading is complete IsFuzzyIndexRecoveryComplete(true); // close index checkpoint files appropriately info.main_ht_device.Close(); info.ofb_device.Close(); // Delete all tentative entries! DeleteTentativeEntries(); }
/// <summary> /// Create default configuration backed by local storage at given base directory. /// Use Utility.ParseSize to specify sizes in familiar string notation (e.g., "4k" and "4 MB"). /// Default index size is 64MB. /// </summary> /// <param name="baseDir">Base directory (without trailing path separator)</param> /// <param name="deleteDirOnDispose">Whether to delete base directory on dispose. This option prevents later recovery.</param> public FasterLogSettings(string baseDir, bool deleteDirOnDispose = false) { disposeDevices = true; this.deleteDirOnDispose = deleteDirOnDispose; this.baseDir = baseDir; LogDevice = baseDir == null ? new NullDevice() : Devices.CreateLogDevice(baseDir + "/fasterlog.log", deleteOnClose: deleteDirOnDispose); LogCommitDir = baseDir; }
/// <inheritdoc /> public IDevice Get(FileDescriptor fileInfo) { var device = Devices.CreateLogDevice(Path.Combine(baseName, fileInfo.directoryName, fileInfo.fileName), preallocateFile: preallocateFile, deleteOnClose: deleteOnClose); if (this.throttleLimit.HasValue) { device.ThrottleLimit = this.throttleLimit.Value; } return(device); }
/// <summary> /// Create default configuration backed by local storage at given base directory. /// Use Utility.ParseSize to specify sizes in familiar string notation (e.g., "4k" and "4 MB"). /// Default index size is 64MB. /// </summary> /// <param name="baseDir">Base directory (without trailing path separator)</param> /// <param name="deleteDirOnDispose">Whether to delete base directory on dispose. This option prevents later recovery.</param> public FasterKVSettings(string baseDir, bool deleteDirOnDispose = false) { disposeDevices = true; this.deleteDirOnDispose = deleteDirOnDispose; this.baseDir = baseDir; LogDevice = baseDir == null ? new NullDevice() : Devices.CreateLogDevice(baseDir + "/hlog.log", deleteOnClose: deleteDirOnDispose); if ((!Utility.IsBlittable <Key>() && KeyLength == null) || (!Utility.IsBlittable <Value>() && ValueLength == null)) { ObjectLogDevice = baseDir == null ? new NullDevice() : Devices.CreateLogDevice(baseDir + "/hlog.obj.log", deleteOnClose: deleteDirOnDispose); } CheckpointDir = baseDir == null ? null : baseDir + "/checkpoints"; }
private void InternalRecover(Guid indexToken, Guid hybridLogToken) { _indexCheckpoint.Recover(indexToken, directoryConfiguration); _hybridLogCheckpoint.Recover(hybridLogToken, directoryConfiguration); // Recover segment offsets for object log if (_hybridLogCheckpoint.info.objectLogSegmentOffsets != null) { Array.Copy(_hybridLogCheckpoint.info.objectLogSegmentOffsets, hlog.GetSegmentOffsets(), _hybridLogCheckpoint.info.objectLogSegmentOffsets.Length); } _indexCheckpoint.main_ht_device = Devices.CreateLogDevice(directoryConfiguration.GetPrimaryHashTableFileName(_indexCheckpoint.info.token), false); _indexCheckpoint.ofb_device = Devices.CreateLogDevice(directoryConfiguration.GetOverflowBucketsFileName(_indexCheckpoint.info.token), false); var l1 = _indexCheckpoint.info.finalLogicalAddress; var l2 = _hybridLogCheckpoint.info.finalLogicalAddress; var v = _hybridLogCheckpoint.info.version; if (l1 > l2) { throw new Exception("Cannot recover from (" + indexToken.ToString() + "," + hybridLogToken.ToString() + ") checkpoint pair!\n"); } _systemState.phase = Phase.REST; _systemState.version = (v + 1); RecoverFuzzyIndex(_indexCheckpoint); IsFuzzyIndexRecoveryComplete(true); DeleteTentativeEntries(); if (FoldOverSnapshot) { RecoverHybridLog(_indexCheckpoint.info, _hybridLogCheckpoint.info); } else { RecoverHybridLogFromSnapshotFile(_indexCheckpoint.info, _hybridLogCheckpoint.info); } _indexCheckpoint.Reset(); RestoreHybridLog(_hybridLogCheckpoint.info.finalLogicalAddress); }
private void RecoverHybridLogFromSnapshotFile( IndexRecoveryInfo indexRecoveryInfo, HybridLogRecoveryInfo recoveryInfo) { var fileStartAddress = recoveryInfo.flushedLogicalAddress; var fromAddress = indexRecoveryInfo.startLogicalAddress; var untilAddress = recoveryInfo.finalLogicalAddress; // Compute startPage and endPage var startPage = hlog.GetPage(fileStartAddress); var endPage = hlog.GetPage(untilAddress); if (untilAddress > hlog.GetStartLogicalAddress(endPage)) { endPage++; } // By default first page has one extra record var capacity = hlog.GetCapacityNumPages(); var recoveryDevice = Devices.CreateLogDevice(directoryConfiguration.GetHybridLogCheckpointFileName(recoveryInfo.guid), false); var objectLogRecoveryDevice = Devices.CreateLogDevice(directoryConfiguration.GetHybridLogObjectCheckpointFileName(recoveryInfo.guid), false); recoveryDevice.Initialize(hlog.GetSegmentSize()); objectLogRecoveryDevice.Initialize(hlog.GetSegmentSize()); var recoveryStatus = new RecoveryStatus(capacity, startPage, endPage, untilAddress) { recoveryDevice = recoveryDevice, objectLogRecoveryDevice = objectLogRecoveryDevice, recoveryDevicePageOffset = startPage }; // Initially issue read request for all pages that can be held in memory int totalPagesToRead = (int)(endPage - startPage); int numPagesToReadFirst = Math.Min(capacity, totalPagesToRead); hlog.AsyncReadPagesFromDevice(startPage, numPagesToReadFirst, untilAddress, AsyncReadPagesCallbackForRecovery, recoveryStatus, recoveryStatus.recoveryDevicePageOffset, recoveryStatus.recoveryDevice, recoveryStatus.objectLogRecoveryDevice); for (long page = startPage; page < endPage; page++) { // Ensure the page is read from file int pageIndex = hlog.GetPageIndexForPage(page); while (recoveryStatus.readStatus[pageIndex] == ReadStatus.Pending) { Thread.Sleep(10); } // Page at hand var startLogicalAddress = hlog.GetStartLogicalAddress(page); var endLogicalAddress = hlog.GetStartLogicalAddress(page + 1); // Perform recovery if page in fuzzy portion of the log if ((fromAddress < endLogicalAddress) && (fromAddress < untilAddress)) { /* * Handling corner-cases: * ---------------------- * When fromAddress is in the middle of the page, * then start recovery only from corresponding offset * in page. Similarly, if untilAddress falls in the * middle of the page, perform recovery only until that * offset. Otherwise, scan the entire page [0, PageSize) */ var pageFromAddress = 0L; if (fromAddress > startLogicalAddress && fromAddress < endLogicalAddress) { pageFromAddress = hlog.GetOffsetInPage(fromAddress); } var pageUntilAddress = hlog.GetPageSize(); if (endLogicalAddress > untilAddress) { pageUntilAddress = hlog.GetOffsetInPage(untilAddress); } var physicalAddress = hlog.GetPhysicalAddress(startLogicalAddress); RecoverFromPage(fromAddress, pageFromAddress, pageUntilAddress, startLogicalAddress, physicalAddress, recoveryInfo.version); } // OS thread flushes current page and issues a read request if necessary recoveryStatus.readStatus[pageIndex] = ReadStatus.Pending; recoveryStatus.flushStatus[pageIndex] = FlushStatus.Pending; // Write back records from snapshot to main hybrid log hlog.AsyncFlushPages(page, 1, AsyncFlushPageCallbackForRecovery, recoveryStatus); } // Assert and wait until all pages have been flushed var done = false; while (!done) { done = true; for (long page = startPage; page < endPage; page++) { int pageIndex = hlog.GetPageIndexForPage(page); if (recoveryStatus.flushStatus[pageIndex] == FlushStatus.Pending) { done = false; break; } } } recoveryStatus.recoveryDevice.Close(); recoveryStatus.objectLogRecoveryDevice.Close(); }
/// <summary> /// Provide device to store snapshot of object log (required only for snapshot checkpoints) /// </summary> /// <param name="token"></param> /// <returns></returns> public IDevice GetSnapshotObjectLogDevice(Guid token) { return(Devices.CreateLogDevice(directoryConfiguration.GetObjectLogSnapshotFileName(token), false)); }
/// <summary> /// Provide device to store index checkpoint (including overflow buckets) /// </summary> /// <param name="indexToken"></param> /// <returns></returns> public IDevice GetIndexDevice(Guid indexToken) { return(Devices.CreateLogDevice(directoryConfiguration.GetPrimaryHashTableFileName(indexToken), false)); }
public void Initialize(Guid token, long _size, DirectoryConfiguration directoryConfiguration) { info.Initialize(token, _size); main_ht_device = Devices.CreateLogDevice(directoryConfiguration.GetPrimaryHashTableFileName(token), false); ofb_device = Devices.CreateLogDevice(directoryConfiguration.GetOverflowBucketsFileName(token), false); }
private bool GlobalMoveToNextState(SystemState currentState, SystemState nextState, ref long context) { var intermediateState = SystemState.Make(Phase.INTERMEDIATE, currentState.version); // Move from S1 to I if (MakeTransition(currentState, intermediateState)) { // Acquired ownership to make the transition from S1 to S2 switch (nextState.phase) { case Phase.PREP_INDEX_CHECKPOINT: { _checkpointType = (CheckpointType)context; switch (_checkpointType) { case CheckpointType.INDEX_ONLY: { _indexCheckpointToken = Guid.NewGuid(); InitializeIndexCheckpoint(_indexCheckpointToken); break; } case CheckpointType.FULL: { var fullCheckpointToken = Guid.NewGuid(); _indexCheckpointToken = fullCheckpointToken; _hybridLogCheckpointToken = fullCheckpointToken; InitializeIndexCheckpoint(_indexCheckpointToken); InitializeHybridLogCheckpoint(_hybridLogCheckpointToken, currentState.version); break; } default: throw new Exception(); } ObtainCurrentTailAddress(ref _indexCheckpoint.info.startLogicalAddress); MakeTransition(intermediateState, nextState); break; } case Phase.INDEX_CHECKPOINT: { TakeIndexFuzzyCheckpoint(); MakeTransition(intermediateState, nextState); break; } case Phase.PREPARE: { switch (currentState.phase) { case Phase.REST: { _checkpointType = (CheckpointType)context; Debug.Assert(_checkpointType == CheckpointType.HYBRID_LOG_ONLY); _hybridLogCheckpointToken = Guid.NewGuid(); InitializeHybridLogCheckpoint(_hybridLogCheckpointToken, currentState.version); break; } case Phase.PREP_INDEX_CHECKPOINT: { TakeIndexFuzzyCheckpoint(); break; } default: throw new Exception(); } ObtainCurrentTailAddress(ref _hybridLogCheckpoint.info.startLogicalAddress); if (!FoldOverSnapshot) { _hybridLogCheckpoint.info.flushedLogicalAddress = hlog.FlushedUntilAddress; _hybridLogCheckpoint.info.useSnapshotFile = 1; } MakeTransition(intermediateState, nextState); break; } case Phase.IN_PROGRESS: { MakeTransition(intermediateState, nextState); break; } case Phase.WAIT_PENDING: { var seg = hlog.GetSegmentOffsets(); if (seg != null) { _hybridLogCheckpoint.info.objectLogSegmentOffsets = new long[seg.Length]; Array.Copy(seg, _hybridLogCheckpoint.info.objectLogSegmentOffsets, seg.Length); } MakeTransition(intermediateState, nextState); break; } case Phase.WAIT_FLUSH: { if (_checkpointType == CheckpointType.FULL) { _indexCheckpoint.info.num_buckets = overflowBucketsAllocator.GetMaxValidAddress(); ObtainCurrentTailAddress(ref _indexCheckpoint.info.finalLogicalAddress); WriteIndexMetaFile(); WriteIndexCheckpointCompleteFile(); } if (FoldOverSnapshot) { hlog.ShiftReadOnlyToTail(out long tailAddress); _hybridLogCheckpoint.info.finalLogicalAddress = tailAddress; } else { ObtainCurrentTailAddress(ref _hybridLogCheckpoint.info.finalLogicalAddress); _hybridLogCheckpoint.snapshotFileDevice = Devices.CreateLogDevice (directoryConfiguration.GetHybridLogCheckpointFileName(_hybridLogCheckpointToken), false); _hybridLogCheckpoint.snapshotFileObjectLogDevice = Devices.CreateLogDevice (directoryConfiguration.GetHybridLogObjectCheckpointFileName(_hybridLogCheckpointToken), false); _hybridLogCheckpoint.snapshotFileDevice.Initialize(hlog.GetSegmentSize()); _hybridLogCheckpoint.snapshotFileObjectLogDevice.Initialize(hlog.GetSegmentSize()); long startPage = hlog.GetPage(_hybridLogCheckpoint.info.flushedLogicalAddress); long endPage = hlog.GetPage(_hybridLogCheckpoint.info.finalLogicalAddress); if (_hybridLogCheckpoint.info.finalLogicalAddress > hlog.GetStartLogicalAddress(endPage)) { endPage++; } // This can be run on a new thread if we want to immediately parallelize // the rest of the log flush hlog.AsyncFlushPagesToDevice(startPage, endPage, _hybridLogCheckpoint.info.finalLogicalAddress, _hybridLogCheckpoint.snapshotFileDevice, _hybridLogCheckpoint.snapshotFileObjectLogDevice, out _hybridLogCheckpoint.flushed); } MakeTransition(intermediateState, nextState); break; } case Phase.PERSISTENCE_CALLBACK: { WriteHybridLogMetaInfo(); WriteHybridLogCheckpointCompleteFile(); MakeTransition(intermediateState, nextState); break; } case Phase.GC: { hlog.ShiftBeginAddress(context); int numChunks = (int)(state[resizeInfo.version].size / Constants.kSizeofChunk); if (numChunks == 0) { numChunks = 1; // at least one chunk } numPendingChunksToBeGCed = numChunks; gcStatus = new long[numChunks]; MakeTransition(intermediateState, nextState); break; } case Phase.PREPARE_GROW: { // Note that the transition must be done before bumping epoch here! MakeTransition(intermediateState, nextState); epoch.BumpCurrentEpoch(() => { long _context = 0; GlobalMoveToNextState(nextState, SystemState.Make(Phase.IN_PROGRESS_GROW, nextState.version), ref _context); }); break; } case Phase.IN_PROGRESS_GROW: { // Set up the transition to new version of HT int numChunks = (int)(state[resizeInfo.version].size / Constants.kSizeofChunk); if (numChunks == 0) { numChunks = 1; // at least one chunk } numPendingChunksToBeSplit = numChunks; splitStatus = new long[numChunks]; Initialize(1 - resizeInfo.version, state[resizeInfo.version].size * 2, sectorSize); resizeInfo.version = 1 - resizeInfo.version; MakeTransition(intermediateState, nextState); break; } case Phase.REST: { switch (_checkpointType) { case CheckpointType.INDEX_ONLY: { _indexCheckpoint.info.num_buckets = overflowBucketsAllocator.GetMaxValidAddress(); ObtainCurrentTailAddress(ref _indexCheckpoint.info.finalLogicalAddress); WriteIndexMetaFile(); WriteIndexCheckpointCompleteFile(); _indexCheckpoint.Reset(); break; } case CheckpointType.FULL: { _indexCheckpoint.Reset(); _hybridLogCheckpoint.Reset(); break; } case CheckpointType.HYBRID_LOG_ONLY: { _hybridLogCheckpoint.Reset(); break; } case CheckpointType.NONE: break; default: throw new Exception(); } _checkpointType = CheckpointType.NONE; MakeTransition(intermediateState, nextState); break; } } return(true); } else { return(false); } }
/// <summary> /// Provide device to store delta log for incremental snapshot checkpoints /// </summary> /// <param name="token"></param> /// <returns></returns> public IDevice GetDeltaLogDevice(Guid token) { return(Devices.CreateLogDevice(directoryConfiguration.GetDeltaLogFileName(token), false)); }
/// <inheritdoc /> public IDevice Get(FileDescriptor fileInfo) { return(Devices.CreateLogDevice(Path.Combine(baseName, fileInfo.directoryName, fileInfo.fileName), preallocateFile: preallocateFile, deleteOnClose: deleteOnClose)); }