// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; namespace FASTER.core { /// /// /// public unsafe partial class FasterBase { internal ICheckpointManager checkpointManager; // 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 = checkpointManager.GetIndexDevice(token); BeginMainIndexRecovery(ht_version, info.main_ht_device, info.info.num_ht_bytes); var sectorSize = info.main_ht_device.SectorSize; var alignedIndexSize = (uint)((info.info.num_ht_bytes + (sectorSize - 1)) & ~(sectorSize - 1)); overflowBucketsAllocator.Recover(info.main_ht_device, alignedIndexSize, 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(); // Delete all tentative entries! DeleteTentativeEntries(); } internal void RecoverFuzzyIndex(int ht_version, IDevice device, ulong num_ht_bytes, IDevice ofbdevice, int num_buckets, ulong num_ofb_bytes) { BeginMainIndexRecovery(ht_version, device, num_ht_bytes); var sectorSize = device.SectorSize; var alignedIndexSize = (uint)((num_ht_bytes + (sectorSize - 1)) & ~(sectorSize - 1)); overflowBucketsAllocator.Recover(ofbdevice, alignedIndexSize, num_buckets, num_ofb_bytes); } internal bool IsFuzzyIndexRecoveryComplete(bool waitUntilComplete = false) { bool completed1 = IsMainIndexRecoveryCompleted(waitUntilComplete); bool completed2 = overflowBucketsAllocator.IsRecoveryCompleted(waitUntilComplete); return completed1 && completed2; } //Main Index Recovery Functions private CountdownEvent mainIndexRecoveryEvent; private void BeginMainIndexRecovery( int version, IDevice device, ulong num_bytes) { int numChunksToBeRecovered = 1; long totalSize = state[version].size * sizeof(HashBucket); Debug.Assert(totalSize < (long)uint.MaxValue); // required since numChunks = 1 uint chunkSize = (uint)(totalSize / numChunksToBeRecovered); mainIndexRecoveryEvent = new CountdownEvent(numChunksToBeRecovered); HashBucket* start = state[version].tableAligned; ulong numBytesRead = 0; for (int index = 0; index < numChunksToBeRecovered; index++) { long chunkStartBucket = (long)start + (index * chunkSize); HashIndexPageAsyncReadResult result = default(HashIndexPageAsyncReadResult); result.chunkIndex = index; device.ReadAsync(numBytesRead, (IntPtr)chunkStartBucket, chunkSize, AsyncPageReadCallback, result); numBytesRead += chunkSize; } Debug.Assert(numBytesRead == num_bytes); } private bool IsMainIndexRecoveryCompleted( bool waitUntilComplete = false) { bool completed = mainIndexRecoveryEvent.IsSet; if (!completed && waitUntilComplete) { mainIndexRecoveryEvent.Wait(); return true; } return completed; } private unsafe void AsyncPageReadCallback(uint errorCode, uint numBytes, NativeOverlapped* overlap) { if (errorCode != 0) { Trace.TraceError("OverlappedStream GetQueuedCompletionStatus error: {0}", errorCode); } mainIndexRecoveryEvent.Signal(); Overlapped.Free(overlap); } internal void DeleteTentativeEntries() { HashBucketEntry entry = default(HashBucketEntry); int version = resizeInfo.version; var table_size_ = state[version].size; var ptable_ = state[version].tableAligned; for (long bucket = 0; bucket < table_size_; ++bucket) { HashBucket b = *(ptable_ + bucket); while (true) { for (int bucket_entry = 0; bucket_entry < Constants.kOverflowBucketIndex; ++bucket_entry) { entry.word = b.bucket_entries[bucket_entry]; if (entry.Tentative) b.bucket_entries[bucket_entry] = 0; } if (b.bucket_entries[Constants.kOverflowBucketIndex] == 0) break; b = *((HashBucket*)overflowBucketsAllocator.GetPhysicalAddress((b.bucket_entries[Constants.kOverflowBucketIndex]))); } } } } }