Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tiered storage device #151

Merged
merged 75 commits into from
Aug 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
18807b2
Implement IDevice backed by Azure Page Blobs
tli2 Jun 26, 2019
db8873e
bug fixes
tli2 Jun 28, 2019
33bb63d
fix bug resulting from -1 segment size
tli2 Jun 28, 2019
5c6c7ad
Merge branch 'master' into page-blob-device
tli2 Jun 28, 2019
b0f7961
Change blob size
tli2 Jun 28, 2019
01918dd
Merge branch 'page-blob-device' of https://github.com/tli2/FASTER int…
tli2 Jun 28, 2019
5453ae9
Change the API to match LocalStorageDevice more closely. Convert some…
tli2 Jul 1, 2019
3de7755
Attempt to spin up Azure Storage Emulator on the C# build pipeline
tli2 Jul 2, 2019
01e5945
Add concept of capacity to devices
tli2 Jul 2, 2019
0be9179
Add docs
tli2 Jul 2, 2019
f84e0a9
Download storage emulator at the start of the pipeline instead.
tli2 Jul 2, 2019
e651ec8
Force reinstall
tli2 Jul 2, 2019
f5e4dbd
Re-enable SQL server intialization to make storage simulator happy
tli2 Jul 2, 2019
b7673f2
WAT
tli2 Jul 2, 2019
e3c75fd
Fix all warnings. Add a step to (hopefully) remove old versions
tli2 Jul 2, 2019
27cb315
Attempt to install to alternative directory
tli2 Jul 2, 2019
8915d0b
attempt to dump log and see what's wrong
tli2 Jul 2, 2019
ba9236d
Correct installation path.
tli2 Jul 2, 2019
636441a
Remove debug log step.
tli2 Jul 2, 2019
d1ff361
Add skeleton implementation for tiered storage device
tli2 Jul 2, 2019
2944ba7
Merge branch 'page-blob-device' into tiered-storage-device
tli2 Jul 2, 2019
b352832
Implement TieredStorageDevice with inclusive policy and no range shif…
tli2 Jul 3, 2019
ca5f149
Merge branch 'master' into page-blob-device
badrishc Jul 4, 2019
948883d
Merge branch 'page-blob-device' into tiered-storage-device
tli2 Jul 5, 2019
f277d61
fix compilation
tli2 Jul 5, 2019
1522911
Draft about how updating of storage range might work.
tli2 Jul 5, 2019
f685971
fix wrong assert
tli2 Jul 8, 2019
c3b2d0c
Fix some bugs and add a simple test case
tli2 Jul 8, 2019
17a577f
Wait for all previous tiers to complete write before invoking callbac…
tli2 Jul 9, 2019
3a75ee7
Merge branch 'master' into page-blob-device
badrishc Jul 10, 2019
7f0ab9c
Merge branch 'master' into tiered-storage-device
badrishc Jul 10, 2019
e4ca284
Integrate epoch protection, refactor for uniform segment sizes
tli2 Jul 10, 2019
77ec994
Merge branch 'tiered-storage-device' of https://github.com/tli2/FASTE…
tli2 Jul 10, 2019
0a87422
Pick out the MonotonicUpdate function into utility
tli2 Jul 10, 2019
48b8182
Handle contention on tier start addresses and read-delete
tli2 Jul 11, 2019
25f3e7a
Refactor IDevice to have a truncate interface instead of deletes. Mov…
tli2 Jul 12, 2019
abd73e9
Move AzurePageBlobDevice into a separate solution.
tli2 Jul 12, 2019
cc02d0e
Remove stale file
tli2 Jul 12, 2019
8752f72
Merge branch 'master' into page-blob-device
tli2 Jul 12, 2019
438a40c
Better exception handling. Dealing with contention on blob creation.
tli2 Jul 15, 2019
a316516
Merge branch 'master' into page-blob-device
tli2 Jul 15, 2019
1373290
minor cleanups
tli2 Jul 15, 2019
34b75e6
Add debug prints to debug Azure pipeline timeout
tli2 Jul 15, 2019
ce131be
Handle silent failure of exception in background thread.
tli2 Jul 15, 2019
fb4a438
does this work?
tli2 Jul 15, 2019
28249d7
Revert to try and locate source of Azure pipeline failure
tli2 Jul 15, 2019
db64037
more attempts
tli2 Jul 15, 2019
5b48ecd
More attempts
tli2 Jul 15, 2019
ad671f6
More attempts
tli2 Jul 15, 2019
1adc2d0
More attempts
tli2 Jul 15, 2019
9c189d1
More attempts
tli2 Jul 15, 2019
705c91c
Try to differentiate between read error and write error
tli2 Jul 16, 2019
728c651
print exception message
tli2 Jul 16, 2019
376d44a
Merge branch 'page-blob-device' into tiered-storage-device
tli2 Jul 16, 2019
e46ea6e
Pick out the MonotonicUpdate function into utility
tli2 Jul 10, 2019
20aec4c
Revert "Pick out the MonotonicUpdate function into utility"
tli2 Jul 16, 2019
7e56bba
Deal with creation race condition
tli2 Jul 16, 2019
f984cc4
Revert debug code
tli2 Jul 16, 2019
ecfb8de
Cleanup and condition test execution
badrishc Jul 17, 2019
d9c529f
minor fix
badrishc Jul 17, 2019
e7e7406
dispose event handles
tli2 Jul 18, 2019
4f248e1
Use an env var for azure tests.
tli2 Jul 18, 2019
744e442
Revert debug code
tli2 Jul 18, 2019
1965983
Merge branch 'page-blob-device' into tiered-storage-device
tli2 Jul 18, 2019
78544af
Merge branch 'master' into page-blob-device
badrishc Jul 23, 2019
cb506e1
Merge branch 'page-blob-device' into tiered-storage-device
tli2 Jul 23, 2019
fbd544b
fix merge problem
tli2 Jul 23, 2019
6895d44
Merge branch 'master' into tiered-storage-device
badrishc Jul 23, 2019
49d31e1
Add recovery of segment range for base (non-composite) devices
tli2 Jul 23, 2019
ce96b50
Merge branch 'tiered-storage-device' of https://github.com/tli2/FASTE…
tli2 Jul 23, 2019
b2b927f
fix incorrect capacity calculation
tli2 Jul 23, 2019
ba68d37
Merge branch 'master' of https://github.com/microsoft/FASTER into tie…
tli2 Jul 24, 2019
6caeac5
fix initialization
tli2 Jul 24, 2019
8bc479d
Merge branch 'master' into tiered-storage-device
badrishc Aug 18, 2019
79f9f95
Merge branch 'master' into tiered-storage-device
badrishc Aug 27, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 19 additions & 45 deletions cs/src/core/Allocator/AllocatorBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -486,8 +486,8 @@ public AllocatorBase(LogSettings settings, IFasterEqualityComparer<Key> comparer
else
this.epoch = epoch;

settings.LogDevice.Initialize(1L << settings.SegmentSizeBits);
settings.ObjectLogDevice?.Initialize(1L << settings.SegmentSizeBits);
settings.LogDevice.Initialize(1L << settings.SegmentSizeBits, epoch);
settings.ObjectLogDevice?.Initialize(1L << settings.SegmentSizeBits, epoch);

// Page size
LogPageSizeBits = settings.PageSizeBits;
Expand Down Expand Up @@ -870,7 +870,7 @@ public void ShiftReadOnlyToTail(out long tailAddress)
tailAddress = GetTailAddress();
long localTailAddress = tailAddress;
long currentReadOnlyOffset = ReadOnlyAddress;
if (MonotonicUpdate(ref ReadOnlyAddress, tailAddress, out long oldReadOnlyOffset))
if (Utility.MonotonicUpdate(ref ReadOnlyAddress, tailAddress, out long oldReadOnlyOffset))
{
epoch.BumpCurrentEpoch(() => OnPagesMarkedReadOnly(localTailAddress, false));
}
Expand All @@ -882,7 +882,7 @@ public void ShiftReadOnlyToTail(out long tailAddress)
/// <param name="newReadOnlyAddress"></param>
public bool ShiftReadOnlyAddress(long newReadOnlyAddress)
{
if (MonotonicUpdate(ref ReadOnlyAddress, newReadOnlyAddress, out long oldReadOnlyOffset))
if (Utility.MonotonicUpdate(ref ReadOnlyAddress, newReadOnlyAddress, out long oldReadOnlyOffset))
{
epoch.BumpCurrentEpoch(() => OnPagesMarkedReadOnly(newReadOnlyAddress, false));
return true;
Expand All @@ -897,34 +897,33 @@ public bool ShiftReadOnlyAddress(long newReadOnlyAddress)
public void ShiftBeginAddress(long newBeginAddress)
{
// First update the begin address
MonotonicUpdate(ref BeginAddress, newBeginAddress, out long oldBeginAddress);
Utility.MonotonicUpdate(ref BeginAddress, newBeginAddress, out long oldBeginAddress);
// Then the head address
var h = MonotonicUpdate(ref HeadAddress, newBeginAddress, out long old);
var h = Utility.MonotonicUpdate(ref HeadAddress, newBeginAddress, out long old);
// Finally the read-only address
var r = MonotonicUpdate(ref ReadOnlyAddress, newBeginAddress, out old);
var r = Utility.MonotonicUpdate(ref ReadOnlyAddress, newBeginAddress, out old);

// Clean up until begin address
epoch.BumpCurrentEpoch(() =>
{
if (r)
{
MonotonicUpdate(ref SafeReadOnlyAddress, newBeginAddress, out long _old);
MonotonicUpdate(ref FlushedUntilAddress, newBeginAddress, out _old);
Utility.MonotonicUpdate(ref SafeReadOnlyAddress, newBeginAddress, out long _old);
Utility.MonotonicUpdate(ref FlushedUntilAddress, newBeginAddress, out _old);
}
if (h) OnPagesClosed(newBeginAddress);

DeleteAddressRange(oldBeginAddress, newBeginAddress);
TruncateUntilAddress(newBeginAddress);
});
}

/// <summary>
/// Delete address range
/// Wraps <see cref="IDevice.TruncateUntilAddress(long)"/> when an allocator potentially has to interact with multiple devices
/// </summary>
/// <param name="fromAddress"></param>
/// <param name="toAddress"></param>
protected virtual void DeleteAddressRange(long fromAddress, long toAddress)
protected virtual void TruncateUntilAddress(long toAddress)
{
device.DeleteAddressRange(fromAddress, toAddress);
device.TruncateUntilAddress(toAddress);
}

/// <summary>
Expand All @@ -935,7 +934,7 @@ protected virtual void DeleteAddressRange(long fromAddress, long toAddress)
/// <param name="waitForPendingFlushComplete"></param>
public void OnPagesMarkedReadOnly(long newSafeReadOnlyAddress, bool waitForPendingFlushComplete = false)
{
if (MonotonicUpdate(ref SafeReadOnlyAddress, newSafeReadOnlyAddress, out long oldSafeReadOnlyAddress))
if (Utility.MonotonicUpdate(ref SafeReadOnlyAddress, newSafeReadOnlyAddress, out long oldSafeReadOnlyAddress))
{
Debug.WriteLine("SafeReadOnly shifted from {0:X} to {1:X}", oldSafeReadOnlyAddress, newSafeReadOnlyAddress);
long startPage = oldSafeReadOnlyAddress >> LogPageSizeBits;
Expand Down Expand Up @@ -964,7 +963,7 @@ public void OnPagesMarkedReadOnly(long newSafeReadOnlyAddress, bool waitForPendi
/// <param name="newSafeHeadAddress"></param>
public void OnPagesClosed(long newSafeHeadAddress)
{
if (MonotonicUpdate(ref SafeHeadAddress, newSafeHeadAddress, out long oldSafeHeadAddress))
if (Utility.MonotonicUpdate(ref SafeHeadAddress, newSafeHeadAddress, out long oldSafeHeadAddress))
{
Debug.WriteLine("SafeHeadOffset shifted from {0:X} to {1:X}", oldSafeHeadAddress, newSafeHeadAddress);

Expand Down Expand Up @@ -1020,7 +1019,7 @@ private void PageAlignedShiftReadOnlyAddress(long currentTailAddress)
long currentReadOnlyAddress = ReadOnlyAddress;
long pageAlignedTailAddress = currentTailAddress & ~PageSizeMask;
long desiredReadOnlyAddress = (pageAlignedTailAddress - ReadOnlyLagAddress);
if (MonotonicUpdate(ref ReadOnlyAddress, desiredReadOnlyAddress, out long oldReadOnlyAddress))
if (Utility.MonotonicUpdate(ref ReadOnlyAddress, desiredReadOnlyAddress, out long oldReadOnlyAddress))
{
Debug.WriteLine("Allocate: Moving read-only offset from {0:X} to {1:X}", oldReadOnlyAddress, desiredReadOnlyAddress);
epoch.BumpCurrentEpoch(() => OnPagesMarkedReadOnly(desiredReadOnlyAddress));
Expand Down Expand Up @@ -1050,7 +1049,7 @@ private void PageAlignedShiftHeadAddress(long currentTailAddress)
if (ReadCache && (newHeadAddress > HeadAddress))
EvictCallback(HeadAddress, newHeadAddress);

if (MonotonicUpdate(ref HeadAddress, newHeadAddress, out long oldHeadAddress))
if (Utility.MonotonicUpdate(ref HeadAddress, newHeadAddress, out long oldHeadAddress))
{
Debug.WriteLine("Allocate: Moving head offset from {0:X} to {1:X}", oldHeadAddress, newHeadAddress);
epoch.BumpCurrentEpoch(() => OnPagesClosed(newHeadAddress));
Expand All @@ -1075,7 +1074,7 @@ public long ShiftHeadAddress(long desiredHeadAddress)
if (ReadCache && (newHeadAddress > HeadAddress))
EvictCallback(HeadAddress, newHeadAddress);

if (MonotonicUpdate(ref HeadAddress, newHeadAddress, out long oldHeadAddress))
if (Utility.MonotonicUpdate(ref HeadAddress, newHeadAddress, out long oldHeadAddress))
{
Debug.WriteLine("Allocate: Moving head offset from {0:X} to {1:X}", oldHeadAddress, newHeadAddress);
epoch.BumpCurrentEpoch(() => OnPagesClosed(newHeadAddress));
Expand Down Expand Up @@ -1104,33 +1103,8 @@ protected void ShiftFlushedUntilAddress()

if (update)
{
MonotonicUpdate(ref FlushedUntilAddress, currentFlushedUntilAddress, out long oldFlushedUntilAddress);
}
}



/// <summary>
/// Used by several functions to update the variable to newValue. Ignores if newValue is smaller or
/// than the current value.
/// </summary>
/// <param name="variable"></param>
/// <param name="newValue"></param>
/// <param name="oldValue"></param>
/// <returns></returns>
private bool MonotonicUpdate(ref long variable, long newValue, out long oldValue)
{
oldValue = variable;
while (oldValue < newValue)
{
var foundValue = Interlocked.CompareExchange(ref variable, newValue, oldValue);
if (foundValue == oldValue)
{
return true;
}
oldValue = foundValue;
Utility.MonotonicUpdate(ref FlushedUntilAddress, currentFlushedUntilAddress, out long oldFlushedUntilAddress);
}
return false;
}

/// <summary>
Expand Down
5 changes: 0 additions & 5 deletions cs/src/core/Allocator/BlittableAllocator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,6 @@ protected override bool IsAllocated(int pageIndex)
return values[pageIndex] != null;
}

protected override void DeleteAddressRange(long fromAddress, long toAddress)
{
base.DeleteAddressRange(fromAddress, toAddress);
}

protected override void WriteAsync<TContext>(long flushPage, IOCompletionCallback callback, PageAsyncFlushResult<TContext> asyncResult)
{
WriteAsync((IntPtr)pointers[flushPage % BufferSize],
Expand Down
6 changes: 3 additions & 3 deletions cs/src/core/Allocator/GenericAllocator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,10 +226,10 @@ protected override bool IsAllocated(int pageIndex)
return values[pageIndex] != null;
}

protected override void DeleteAddressRange(long fromAddress, long toAddress)
protected override void TruncateUntilAddress(long toAddress)
{
base.DeleteAddressRange(fromAddress, toAddress);
objectLogDevice.DeleteSegmentRange((int)(fromAddress >> LogSegmentSizeBits), (int)(toAddress >> LogSegmentSizeBits));
base.TruncateUntilAddress(toAddress);
objectLogDevice.TruncateUntilAddress(toAddress);
}

protected override void WriteAsync<TContext>(long flushPage, IOCompletionCallback callback, PageAsyncFlushResult<TContext> asyncResult)
Expand Down
5 changes: 0 additions & 5 deletions cs/src/core/Allocator/VarLenBlittableAllocator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,11 +225,6 @@ protected override bool IsAllocated(int pageIndex)
return values[pageIndex] != null;
}

protected override void DeleteAddressRange(long fromAddress, long toAddress)
{
base.DeleteAddressRange(fromAddress, toAddress);
}

protected override void WriteAsync<TContext>(long flushPage, IOCompletionCallback callback, PageAsyncFlushResult<TContext> asyncResult)
{
WriteAsync((IntPtr)pointers[flushPage % BufferSize],
Expand Down
14 changes: 11 additions & 3 deletions cs/src/core/Device/Devices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,22 @@ namespace FASTER.core
/// </summary>
public static class Devices
{
/// <summary>
/// This value is supplied for capacity when the device does not have a specified limit.
/// </summary>
public const long CAPACITY_UNSPECIFIED = -1;
private const string EMULATED_STORAGE_STRING = "UseDevelopmentStorage=true;";
private const string TEST_CONTAINER = "test";

/// <summary>
/// Create a storage device for the log
/// </summary>
/// <param name="logPath">Path to file that will store the log (empty for null device)</param>
/// <param name="preallocateFile">Whether we try to preallocate the file on creation</param>
/// <param name="deleteOnClose">Delete files on close</param>
/// <param name="capacity"></param>
/// <returns>Device instance</returns>
public static IDevice CreateLogDevice(string logPath, bool preallocateFile = true, bool deleteOnClose = false)
public static IDevice CreateLogDevice(string logPath, bool preallocateFile = true, bool deleteOnClose = false, long capacity = CAPACITY_UNSPECIFIED)
{
if (string.IsNullOrWhiteSpace(logPath))
return new NullDevice();
Expand All @@ -30,12 +38,12 @@ public static IDevice CreateLogDevice(string logPath, bool preallocateFile = tru
#if DOTNETCORE
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
logDevice = new ManagedLocalStorageDevice(logPath, preallocateFile, deleteOnClose);
logDevice = new ManagedLocalStorageDevice(logPath, preallocateFile, deleteOnClose, capacity);
}
else
#endif
{
logDevice = new LocalStorageDevice(logPath, preallocateFile, deleteOnClose);
logDevice = new LocalStorageDevice(logPath, preallocateFile, deleteOnClose, capacity: capacity);
}
return logDevice;
}
Expand Down
93 changes: 78 additions & 15 deletions cs/src/core/Device/IDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,44 @@ public interface IDevice
string FileName { get; }

/// <summary>
/// Initialize device
/// Returns the maximum capacity of the storage device, in number of bytes.
/// If returned CAPACITY_UNSPECIFIED, the storage device has no specfied capacity limit.
/// </summary>
/// <param name="segmentSize"></param>
void Initialize(long segmentSize);
long Capacity { get; }

/// <summary>
/// A device breaks up each logical log into multiple self-contained segments that are of the same size.
/// It is an atomic unit of data that cannot be partially present on a device (i.e. either the entire segment
/// is present or no data from the segment is present). Examples of this include files or named blobs. This
/// property returns the size of each segment.
/// </summary>
long SegmentSize { get; }

/* Segmented addressing API */
/// <summary>
/// The index of the first segment present on this device
/// </summary>
int StartSegment { get; }

/// <summary>
/// The index of the last segment present on this device
/// </summary>
int EndSegment { get; }

/// <summary>
/// Initialize device. This function is used to pass optional information that may only be known after
/// FASTER initialization (whose constructor takes in IDevice upfront). Implementation are free to ignore
/// information if it does not need the supplied information.
///
/// This is a bit of a hack.
/// </summary>
/// <param name="segmentSize"></param>
/// <param name="epoch">
/// The instance of the epoch protection framework to use, if needed
/// </param>
void Initialize(long segmentSize, LightEpoch epoch = null);


/* Segmented addressing API */
/// <summary>
/// Write
/// </summary>
Expand All @@ -52,13 +82,6 @@ public interface IDevice
/// <param name="asyncResult"></param>
void ReadAsync(int segmentId, ulong sourceAddress, IntPtr destinationAddress, uint readLength, IOCompletionCallback callback, IAsyncResult asyncResult);

/// <summary>
/// Delete segment range
/// </summary>
/// <param name="fromSegment"></param>
/// <param name="toSegment"></param>
void DeleteSegmentRange(int fromSegment, int toSegment);

/* Direct addressing API */

/// <summary>
Expand All @@ -82,11 +105,51 @@ public interface IDevice
void ReadAsync(ulong alignedSourceAddress, IntPtr alignedDestinationAddress, uint aligned_read_length, IOCompletionCallback callback, IAsyncResult asyncResult);

/// <summary>
/// Delete address range
/// Truncates the log until the given address. The truncated portion should no longer be accessed as the device is no longer responsible for
/// its maintenance, but physical deletion may not happen immediately.
/// </summary>
/// <param name="toAddress">upper bound of truncated address</param>
/// <param name="callback">callback to invoke when truncation is complete</param>
/// <param name="result">result to be passed to the callback</param>
void TruncateUntilAddressAsync(long toAddress, AsyncCallback callback, IAsyncResult result);

/// <summary>
/// Truncates the log until the given address. The truncated portion should no longer be accessed as the device is no longer responsible for
/// its maintenance, but physical deletion may not happen immediately. This version of the function can block.
/// </summary>
/// <param name="toAddress">upper bound of truncated address</param>
void TruncateUntilAddress(long toAddress);

/// <summary>
/// Truncates the log until the given segment. Physical deletion of the given segments are guaranteed to have happened when the callback is invoked.
/// </summary>
/// <param name="toSegment">the largest (in index) segment to truncate</param>
/// <param name="callback">callback to invoke when truncation is complete</param>
/// <param name="result">result to be passed to the callback</param>
void TruncateUntilSegmentAsync(int toSegment, AsyncCallback callback, IAsyncResult result);

/// <summary>
/// Truncates the log until the given segment. Physical deletion of the given segments are guaranteed to have happened when the function returns.
/// This version of the function can block.
/// </summary>
/// <param name="toSegment">the largest (in index) segment to truncate</param>
void TruncateUntilSegment(int toSegment);

/// <summary>
/// Removes a single segment from the device. This function should not normally be called.
/// Instead, use <see cref="TruncateUntilAddressAsync(long, AsyncCallback, IAsyncResult)"/>
/// </summary>
/// <param name="segment">index of the segment to remov</param>
/// <param name="callback">callback to invoke when removal is complete</param>
/// <param name="result">result to be passed to the callback</param>
void RemoveSegmentAsync(int segment, AsyncCallback callback, IAsyncResult result);

/// <summary>
/// Removes a single segment from the device. This function should not normally be called.
/// Instead, use <see cref="TruncateUntilAddressAsync(long, AsyncCallback, IAsyncResult)"/>
/// </summary>
/// <param name="fromAddress"></param>
/// <param name="toAddress"></param>
void DeleteAddressRange(long fromAddress, long toAddress);
/// <param name="segment">index of the segment to remov</param>
void RemoveSegment(int segment);

/* Close */

Expand Down
Loading