Skip to content

Commit

Permalink
[C#] Allow recovery without index checkpoint (#328)
Browse files Browse the repository at this point in the history
* Allow recovery without index checkpoint, by simply using an empty hash table and replaying the log from the begin address.

* Remove NullDevice as default device in LogSettings. Make sure users do not instantiate FASTER without the necessary devices, in order to avoid unexpected behavior.
  • Loading branch information
badrishc committed Sep 12, 2020
1 parent 9223b1c commit 0fc5689
Show file tree
Hide file tree
Showing 9 changed files with 51 additions and 20 deletions.
4 changes: 4 additions & 0 deletions cs/src/core/Allocator/AllocatorBase.cs
Expand Up @@ -484,6 +484,10 @@ public unsafe abstract partial class AllocatorBase<Key, Value> : IDisposable
/// <param name="flushCallback"></param>
public AllocatorBase(LogSettings settings, IFasterEqualityComparer<Key> comparer, Action<long, long> evictCallback, LightEpoch epoch, Action<CommitInfo> flushCallback)
{
if (settings.LogDevice == null)
{
throw new FasterException("LogSettings.LogDevice needs to be specified (e.g., use Devices.CreateLogDevice, AzureStorageDevice, or NullDevice)");
}
if (evictCallback != null)
{
ReadCache = true;
Expand Down
5 changes: 5 additions & 0 deletions cs/src/core/Allocator/GenericAllocator.cs
Expand Up @@ -42,6 +42,11 @@ public unsafe sealed class GenericAllocator<Key, Value> : AllocatorBase<Key, Val
public GenericAllocator(LogSettings settings, SerializerSettings<Key, Value> serializerSettings, IFasterEqualityComparer<Key> comparer, Action<long, long> evictCallback = null, LightEpoch epoch = null, Action<CommitInfo> flushCallback = null)
: base(settings, comparer, evictCallback, epoch, flushCallback)
{
if (settings.ObjectLogDevice == null)
{
throw new FasterException("LogSettings.ObjectLogDevice needs to be specified (e.g., use Devices.CreateLogDevice, AzureStorageDevice, or NullDevice)");
}

SerializerSettings = serializerSettings;

if ((!keyBlittable) && (settings.LogDevice as NullDevice == null) && ((SerializerSettings == null) || (SerializerSettings.keySerializer == null)))
Expand Down
3 changes: 0 additions & 3 deletions cs/src/core/Device/Devices.cs
Expand Up @@ -26,9 +26,6 @@ public static class Devices
/// <returns>Device instance</returns>
public static IDevice CreateLogDevice(string logPath, bool preallocateFile = false, bool deleteOnClose = false, long capacity = CAPACITY_UNSPECIFIED, bool recoverDevice = false)
{
if (string.IsNullOrWhiteSpace(logPath))
return new NullDevice();

IDevice logDevice;

#if DOTNETCORE
Expand Down
4 changes: 2 additions & 2 deletions cs/src/core/Index/Common/LogSettings.cs
Expand Up @@ -32,12 +32,12 @@ public class LogSettings
/// <summary>
/// Device used for main hybrid log
/// </summary>
public IDevice LogDevice = new NullDevice();
public IDevice LogDevice;

/// <summary>
/// Device used for serialized heap objects in hybrid log
/// </summary>
public IDevice ObjectLogDevice = new NullDevice();
public IDevice ObjectLogDevice;

/// <summary>
/// Size of a segment (group of pages), in bits
Expand Down
4 changes: 4 additions & 0 deletions cs/src/core/Index/FASTER/FASTER.cs
Expand Up @@ -144,6 +144,7 @@ public partial class FasterKV<Key, Value> : FasterBase,
readcache = new VariableLengthBlittableAllocator<Key, Value>(
new LogSettings
{
LogDevice = new NullDevice(),
PageSizeBits = logSettings.ReadCacheSettings.PageSizeBits,
MemorySizeBits = logSettings.ReadCacheSettings.MemorySizeBits,
SegmentSizeBits = logSettings.ReadCacheSettings.MemorySizeBits,
Expand All @@ -162,6 +163,7 @@ public partial class FasterKV<Key, Value> : FasterBase,
readcache = new BlittableAllocator<Key, Value>(
new LogSettings
{
LogDevice = new NullDevice(),
PageSizeBits = logSettings.ReadCacheSettings.PageSizeBits,
MemorySizeBits = logSettings.ReadCacheSettings.MemorySizeBits,
SegmentSizeBits = logSettings.ReadCacheSettings.MemorySizeBits,
Expand All @@ -183,6 +185,8 @@ public partial class FasterKV<Key, Value> : FasterBase,
readcache = new GenericAllocator<Key, Value>(
new LogSettings
{
LogDevice = new NullDevice(),
ObjectLogDevice = new NullDevice(),
PageSizeBits = logSettings.ReadCacheSettings.PageSizeBits,
MemorySizeBits = logSettings.ReadCacheSettings.MemorySizeBits,
SegmentSizeBits = logSettings.ReadCacheSettings.MemorySizeBits,
Expand Down
2 changes: 1 addition & 1 deletion cs/src/core/Index/FASTER/FASTERIterator.cs
Expand Up @@ -92,7 +92,7 @@ public FasterKVIterator(FasterKV<Key, Value> fht, Functions functions, Compactio
this.cf = cf;
enumerationPhase = 0;
fhtSession = fht.NewSession<Empty, Empty, Empty, Functions>(functions);
tempKv = new FasterKV<Key, Value>(fht.IndexSize, new LogSettings { MutableFraction = 1 }, comparer: fht.Comparer, variableLengthStructSettings: variableLengthStructSettings);
tempKv = new FasterKV<Key, Value>(fht.IndexSize, new LogSettings { LogDevice = new NullDevice(), ObjectLogDevice = new NullDevice(), MutableFraction = 1 }, comparer: fht.Comparer, variableLengthStructSettings: variableLengthStructSettings);
tempKvSession = tempKv.NewSession<Empty, Empty, Empty, Functions>(functions);
iter1 = fht.Log.Scan(fht.Log.BeginAddress, untilAddress);
}
Expand Down
2 changes: 1 addition & 1 deletion cs/src/core/Index/FASTER/LogAccessor.cs
Expand Up @@ -253,7 +253,7 @@ public void Compact<CompactionFunctions>(CompactionFunctions compactionFunctions
var originalUntilAddress = untilAddress;

using (var fhtSession = fht.NewSession<Empty, Empty, Empty, Functions>(functions))
using (var tempKv = new FasterKV<Key, Value>(fht.IndexSize, new LogSettings(), comparer: fht.Comparer, variableLengthStructSettings: variableLengthStructSettings))
using (var tempKv = new FasterKV<Key, Value>(fht.IndexSize, new LogSettings { LogDevice = new NullDevice(), ObjectLogDevice = new NullDevice() }, comparer: fht.Comparer, variableLengthStructSettings: variableLengthStructSettings))
using (var tempKvSession = tempKv.NewSession<Empty, Empty, Empty, Functions>(functions))
{
using (var iter1 = fht.Log.Scan(fht.Log.BeginAddress, untilAddress))
Expand Down
4 changes: 3 additions & 1 deletion cs/src/core/Index/Recovery/IndexRecovery.cs
Expand Up @@ -27,7 +27,9 @@ 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);

if (state[ht_version].size != info.info.table_size)
throw new FasterException($"Incompatible hash table size during recovery; allocated {state[ht_version].size} buckets, recovering {info.info.table_size} buckets");

// Create devices to read from using Async API
info.main_ht_device = checkpointManager.GetIndexDevice(token);
Expand Down
43 changes: 31 additions & 12 deletions cs/src/core/Index/Recovery/Recovery.cs
Expand Up @@ -98,14 +98,14 @@ private void InternalRecoverFromLatestCheckpoints(int numPagesToPreload)
}

Debug.WriteLine("Index Checkpoint: {0}", indexToken);
recoveredICInfo.info.DebugPrint();
break;
}

if (recoveredICInfo.IsDefault())
throw new FasterException("Unable to find valid index token");

recoveredICInfo.info.DebugPrint();

{
Debug.WriteLine("No index checkpoint found, recovering from beginning of log");
}

InternalRecover(recoveredICInfo, recoveredHLCInfo, numPagesToPreload);
}
Expand All @@ -125,18 +125,33 @@ private void InternalRecover(Guid indexToken, Guid hybridLogToken, int numPagesT


// Recovery appropriate context information
var recoveredICInfo = new IndexCheckpointInfo();
recoveredICInfo.Recover(indexToken, checkpointManager);
recoveredICInfo.info.DebugPrint();

var recoveredHLCInfo = new HybridLogCheckpointInfo();
recoveredHLCInfo.Recover(hybridLogToken, checkpointManager);
recoveredHLCInfo.info.DebugPrint();

// Check if the two checkpoints are compatible for recovery
if (!IsCompatible(recoveredICInfo.info, recoveredHLCInfo.info))
IndexCheckpointInfo recoveredICInfo;
try
{
throw new FasterException("Cannot recover from (" + indexToken.ToString() + "," + hybridLogToken.ToString() + ") checkpoint pair!\n");
recoveredICInfo = new IndexCheckpointInfo();
recoveredICInfo.Recover(indexToken, checkpointManager);
recoveredICInfo.info.DebugPrint();
}
catch
{
recoveredICInfo = default;
}

if (recoveredICInfo.IsDefault())
{
Debug.WriteLine("Invalid index checkpoint token, recovering from beginning of log");
}
else
{
// Check if the two checkpoints are compatible for recovery
if (!IsCompatible(recoveredICInfo.info, recoveredHLCInfo.info))
{
throw new FasterException("Cannot recover from (" + indexToken.ToString() + "," + hybridLogToken.ToString() + ") checkpoint pair!\n");
}
}

InternalRecover(recoveredICInfo, recoveredHLCInfo, numPagesToPreload);
Expand All @@ -153,7 +168,11 @@ private void InternalRecover(IndexCheckpointInfo recoveredICInfo, HybridLogCheck
systemState.version = (v + 1);

// Recover fuzzy index from checkpoint
RecoverFuzzyIndex(recoveredICInfo);
if (recoveredICInfo.IsDefault())
recoveredICInfo.info.startLogicalAddress = recoveredHLCInfo.info.beginAddress;
else
RecoverFuzzyIndex(recoveredICInfo);


// Recover segment offsets for object log
if (recoveredHLCInfo.info.objectLogSegmentOffsets != null)
Expand Down

0 comments on commit 0fc5689

Please sign in to comment.