// 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;
}
}
}