// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Threading; namespace FASTER.core { /// /// Implementation of checkpoint interface for local file storage /// public class LocalCheckpointManager : ICheckpointManager { private DirectoryConfiguration directoryConfiguration; /// /// Create new instance of local checkpoint manager at given base directory /// /// public LocalCheckpointManager(string CheckpointDir) { directoryConfiguration = new DirectoryConfiguration(CheckpointDir); } /// /// Initialize index checkpoint /// /// public void InitializeIndexCheckpoint(Guid indexToken) { directoryConfiguration.CreateIndexCheckpointFolder(indexToken); } /// /// Initialize log checkpoint (snapshot and fold-over) /// /// public void InitializeLogCheckpoint(Guid logToken) { directoryConfiguration.CreateHybridLogCheckpointFolder(logToken); } /// /// Commit index checkpoint /// /// /// public void CommitIndexCheckpoint(Guid indexToken, byte[] commitMetadata) { string filename = directoryConfiguration.GetIndexCheckpointMetaFileName(indexToken); using (var writer = new BinaryWriter(new FileStream(filename, FileMode.Create))) { writer.Write(commitMetadata.Length); writer.Write(commitMetadata); writer.Flush(); } string completed_filename = directoryConfiguration.GetIndexCheckpointFolder(indexToken); completed_filename += Path.DirectorySeparatorChar + "completed.dat"; using (var file = new FileStream(completed_filename, FileMode.Create)) { file.Flush(); } } /// /// Commit log checkpoint (snapshot and fold-over) /// /// /// public void CommitLogCheckpoint(Guid logToken, byte[] commitMetadata) { string filename = directoryConfiguration.GetHybridLogCheckpointMetaFileName(logToken); using (var writer = new BinaryWriter(new FileStream(filename, FileMode.Create))) { writer.Write(commitMetadata.Length); writer.Write(commitMetadata); writer.Flush(); } string completed_filename = directoryConfiguration.GetHybridLogCheckpointFolder(logToken); completed_filename += Path.DirectorySeparatorChar + "completed.dat"; using (var file = new FileStream(completed_filename, FileMode.Create)) { file.Flush(); } } /// /// Retrieve commit metadata for specified index checkpoint /// /// Token /// Metadata, or null if invalid public byte[] GetIndexCommitMetadata(Guid indexToken) { var dir = new DirectoryInfo(directoryConfiguration.GetIndexCheckpointFolder(indexToken)); if (!File.Exists(dir.FullName + Path.DirectorySeparatorChar + "completed.dat")) return null; string filename = directoryConfiguration.GetIndexCheckpointMetaFileName(indexToken); using (var reader = new BinaryReader(new FileStream(filename, FileMode.Open))) { var len = reader.ReadInt32(); return reader.ReadBytes(len); } } /// /// Retrieve commit metadata for specified log checkpoint /// /// Token /// Metadata, or null if invalid public byte[] GetLogCommitMetadata(Guid logToken) { var dir = new DirectoryInfo(directoryConfiguration.GetHybridLogCheckpointFolder(logToken)); if (!File.Exists(dir.FullName + Path.DirectorySeparatorChar + "completed.dat")) return null; string checkpointInfoFile = directoryConfiguration.GetHybridLogCheckpointMetaFileName(logToken); using (var reader = new BinaryReader(new FileStream(checkpointInfoFile, FileMode.Open))) { var len = reader.ReadInt32(); return reader.ReadBytes(len); } } /// /// Provide device to store index checkpoint (including overflow buckets) /// /// /// public IDevice GetIndexDevice(Guid indexToken) { return Devices.CreateLogDevice(directoryConfiguration.GetPrimaryHashTableFileName(indexToken), false); } /// /// Provide device to store snapshot of log (required only for snapshot checkpoints) /// /// /// public IDevice GetSnapshotLogDevice(Guid token) { return Devices.CreateLogDevice(directoryConfiguration.GetLogSnapshotFileName(token), false); } /// /// Provide device to store snapshot of object log (required only for snapshot checkpoints) /// /// /// public IDevice GetSnapshotObjectLogDevice(Guid token) { return Devices.CreateLogDevice(directoryConfiguration.GetObjectLogSnapshotFileName(token), false); } /// /// Get latest valid checkpoint for recovery /// /// /// /// public bool GetLatestCheckpoint(out Guid indexToken, out Guid logToken) { var indexCheckpointDir = new DirectoryInfo(directoryConfiguration.GetIndexCheckpointFolder()); var dirs = indexCheckpointDir.GetDirectories(); foreach (var dir in dirs) { // Remove incomplete checkpoints if (!File.Exists(dir.FullName + Path.DirectorySeparatorChar + "completed.dat")) { Directory.Delete(dir.FullName, true); } } var latestICFolder = indexCheckpointDir.GetDirectories().OrderByDescending(f => f.LastWriteTime).First(); if (latestICFolder == null || !Guid.TryParse(latestICFolder.Name, out indexToken)) { throw new Exception("No valid index checkpoint to recover from"); } var hlogCheckpointDir = new DirectoryInfo(directoryConfiguration.GetHybridLogCheckpointFolder()); dirs = hlogCheckpointDir.GetDirectories(); foreach (var dir in dirs) { // Remove incomplete checkpoints if (!File.Exists(dir.FullName + Path.DirectorySeparatorChar + "completed.dat")) { Directory.Delete(dir.FullName, true); } } var latestHLCFolder = hlogCheckpointDir.GetDirectories().OrderByDescending(f => f.LastWriteTime).First(); if (latestHLCFolder == null || !Guid.TryParse(latestHLCFolder.Name, out logToken)) { throw new Exception("No valid hybrid log checkpoint to recover from"); } return true; } } }