Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
3aa431d
First implementation of new SI API
TedHartMS Feb 17, 2021
97bcf90
Add I(Advanced)Functions.Lock/Unlock and minimize SI-related locking
TedHartMS Feb 19, 2021
1b10d64
some experimental stuff to test
TedHartMS Feb 21, 2021
d9db397
Experiment with SupportsLocks
TedHartMS Feb 21, 2021
c54018c
Change to new locking scheme; update test Functions to derive from Fu…
TedHartMS Feb 24, 2021
047e27d
Additional locking work:
TedHartMS Feb 25, 2021
85c54a4
Update YCSB benchmark to separate locking and indexing parameters and…
TedHartMS Feb 25, 2021
11c0289
updates
badrishc Feb 26, 2021
ac12836
Improvments to multi-iteration runs
TedHartMS Feb 26, 2021
2c94bd7
Remove FKV.SupportsMutableIndexes; change benchmark to resuse loaded …
TedHartMS Feb 27, 2021
e340da7
Benchmark: remove interface and make store, device, keys readonly
TedHartMS Feb 27, 2021
a094f81
distinct delimiter on last and trimmed stats summary
TedHartMS Feb 27, 2021
3b118b8
More YCSB benchmark updates:
TedHartMS Mar 1, 2021
ab625ac
Update benchmark to start in unison.
badrishc Mar 3, 2021
5ac4a8c
Refactor YCSB to clean up program.cs and duplicate LoadData and impro…
TedHartMS Mar 3, 2021
e6baec2
Fix UpdateSIForIPU return; fix latchDest in InternalRMW; tweak addres…
TedHartMS Mar 4, 2021
b922709
turn off smalldata for Release
TedHartMS Mar 4, 2021
1874a7c
YCSB changes:
TedHartMS Mar 5, 2021
eb59000
YCSB: change --backup to --recover and remove BackupMode; add --runsec
TedHartMS Mar 9, 2021
664e7dc
a few minor tweaks made while doing PRs
TedHartMS Mar 10, 2021
d15c6c6
Add Powershell scripts to run the benchmark performance suite and com…
TedHartMS Mar 17, 2021
3bb74ad
Merge upstream master
TedHartMS Mar 17, 2021
ef80838
updates to benchmark scripts
TedHartMS Mar 17, 2021
b12d4d6
Merge remote-tracking branch 'origin/master' into SecondaryIndex
TedHartMS Mar 27, 2021
2ce51aa
Add SubsetIndexSessionBroker; add SimpleIndex tests
TedHartMS Mar 28, 2021
c179dc8
Implement ReadOnlyObserver for SI; add Immutable and Mixed index test…
TedHartMS Mar 29, 2021
42619d7
Support multiple ReadOnly and Eviction Observers
TedHartMS Mar 31, 2021
25730b9
Merge upstream master
TedHartMS Apr 14, 2021
01220cf
more files from merge
TedHartMS Apr 14, 2021
e30da07
Merge remote-tracking branch 'origin/master' into SecondaryIndex
TedHartMS Apr 15, 2021
ab1c815
Add RecordId struct; move QueryRecord into SI from HVI; remove record…
TedHartMS Apr 16, 2021
8c88c1a
Add QueryRecord.RecordId
TedHartMS Apr 17, 2021
3b57ef1
RecordId changes from HVI
TedHartMS Apr 17, 2021
f024f3a
QueryRecord changes from HVI
TedHartMS Apr 18, 2021
074028b
Fixes to multi-observer cleanup; fix recordInfo setting in ReadAsyncI…
TedHartMS May 5, 2021
5bf46d9
merge upstream master
TedHartMS May 5, 2021
4458616
Modify Secondary Index interfaces:
TedHartMS May 5, 2021
881f4bd
Merge upstream master
TedHartMS May 26, 2021
32320de
Merge upstream master; backport from HVI branch
TedHartMS Jun 14, 2021
f6a9603
Merge remote-tracking branch 'origin/master' into SecondaryIndex
TedHartMS Jun 14, 2021
101aff6
allow deltaFileDevice == null
TedHartMS Jun 15, 2021
8a321d9
Additional changes for C#9
TedHartMS Jun 15, 2021
d380af8
Backport a few changes from HVI in preparation to merge master
TedHartMS Jul 21, 2021
5a40baa
Merge upstream master
TedHartMS Jul 21, 2021
a2a55ac
merge upstream master
TedHartMS Sep 24, 2021
7917538
add missing OnRecovery to InternalRecoverAsync
TedHartMS Sep 24, 2021
25f310f
Add default values for scanDelta, recoverTo args
TedHartMS Sep 24, 2021
c4b0781
backport mostly doc-related changes from HVI
TedHartMS Sep 29, 2021
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
2 changes: 1 addition & 1 deletion cs/benchmark/FasterSpanByteYcsbBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ public unsafe void Run()
long startTailAddress = store.Log.TailAddress;
Console.WriteLine("Start tail address = " + startTailAddress);

if (!storeWasRecovered && this.backupMode.HasFlag(BackupMode.Backukp) && kPeriodicCheckpointMilliseconds <= 0)
if (!storeWasRecovered && this.backupMode.HasFlag(BackupMode.Backup) && kPeriodicCheckpointMilliseconds <= 0)
{
Console.WriteLine("Checkpointing FasterKV for fast restart");
store.TakeFullCheckpoint(out _);
Expand Down
21 changes: 17 additions & 4 deletions cs/benchmark/FasterYcsbBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public enum Op : ulong

volatile bool done = false;

public FASTER_YcsbBenchmark(int threadCount_, int numaStyle_, string distribution_, int readPercent_, int backupOptions_)
public FASTER_YcsbBenchmark(int threadCount_, int numaStyle_, string distribution_, int readPercent_, int backupOptions_, int indexType_)
{
// Pin loading thread if it is not used for checkpointing
if (kPeriodicCheckpointMilliseconds <= 0)
Expand Down Expand Up @@ -110,13 +110,26 @@ public FASTER_YcsbBenchmark(int threadCount_, int numaStyle_, string distributio

var path = "D:\\data\\FasterYcsbBenchmark\\";
device = Devices.CreateLogDevice(path + "hlog", preallocateFile: true);
var secondaryIndexType = (SecondaryIndexType)indexType_;

if (kSmallMemoryLog)
store = new FasterKV<Key, Value>
(kMaxKey / 2, new LogSettings { LogDevice = device, PreallocateLog = true, PageSizeBits = 22, SegmentSizeBits = 26, MemorySizeBits = 26 }, new CheckpointSettings { CheckPointType = CheckpointType.FoldOver, CheckpointDir = path });
(kMaxKey / 2, new LogSettings { LogDevice = device, PreallocateLog = true, PageSizeBits = 22, SegmentSizeBits = 26, MemorySizeBits = 26 },
new CheckpointSettings { CheckPointType = CheckpointType.FoldOver, CheckpointDir = path },
supportsMutableIndexes: secondaryIndexType != SecondaryIndexType.None);
else
store = new FasterKV<Key, Value>
(kMaxKey / 2, new LogSettings { LogDevice = device, PreallocateLog = true }, new CheckpointSettings { CheckPointType = CheckpointType.FoldOver, CheckpointDir = path });
(kMaxKey / 2, new LogSettings { LogDevice = device, PreallocateLog = true },
new CheckpointSettings { CheckPointType = CheckpointType.FoldOver, CheckpointDir = path },
supportsMutableIndexes: secondaryIndexType != SecondaryIndexType.None);

if (secondaryIndexType != SecondaryIndexType.None)
{
if (secondaryIndexType.HasFlag(SecondaryIndexType.Key))
store.SecondaryIndexBroker.AddIndex(new NullKeyIndex<Key>());
if (secondaryIndexType.HasFlag(SecondaryIndexType.Value))
store.SecondaryIndexBroker.AddIndex(new NullValueIndex<Value>());
}
}

private void RunYcsb(int thread_idx)
Expand Down Expand Up @@ -295,7 +308,7 @@ public unsafe void Run()
long startTailAddress = store.Log.TailAddress;
Console.WriteLine("Start tail address = " + startTailAddress);

if (!storeWasRecovered && this.backupMode.HasFlag(BackupMode.Backukp) && kPeriodicCheckpointMilliseconds <= 0)
if (!storeWasRecovered && this.backupMode.HasFlag(BackupMode.Backup) && kPeriodicCheckpointMilliseconds <= 0)
{
Console.WriteLine("Checkpointing FasterKV for fast restart");
store.TakeFullCheckpoint(out _);
Expand Down
18 changes: 16 additions & 2 deletions cs/benchmark/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ class Options
"\n 3 = Both (Recover FasterKV if the Checkpoint is available, else populate FasterKV from data and Checkpoint it so it can be Restored in a subsequent run)")]
public int Backup { get; set; }

[Option('i', "index", Required = false, Default = 0,
HelpText = "Secondary index type(s); these implement a no-op index to test the overhead on FasterKV operations:" +
"\n 0 = None (default)" +
"\n 1 = Key-based index" +
"\n 2 = Value-based index" +
"\n 3 = Both index types")]
public int SecondaryIndexType { get; set; }

[Option('r', "read_percent", Required = false, Default = 50,
HelpText = "Percentage of reads (-1 for 100% read-modify-write")]
public int ReadPercent { get; set; }
Expand All @@ -44,7 +52,13 @@ enum BenchmarkType : int

[Flags] enum BackupMode : int
{
None, Restore, Backukp, Both
None, Restore, Backup, Both
};

[Flags]
enum SecondaryIndexType : int
{
None, Key, Value, Both
};

public class Program
Expand All @@ -62,7 +76,7 @@ public static void Main(string[] args)

if (b == BenchmarkType.Ycsb)
{
var test = new FASTER_YcsbBenchmark(options.ThreadCount, options.NumaStyle, options.Distribution, options.ReadPercent, options.Backup);
var test = new FASTER_YcsbBenchmark(options.ThreadCount, options.NumaStyle, options.Distribution, options.ReadPercent, options.Backup, options.SecondaryIndexType);
test.Run();
}
else if (b == BenchmarkType.SpanByte)
Expand Down
33 changes: 33 additions & 0 deletions cs/benchmark/SecondaryIndexes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using FASTER.core;

namespace FASTER.benchmark
{
class NullKeyIndex<Key> : ISecondaryKeyIndex<Key>
{
public string Name => "KeyIndex";

public bool IsMutable => true;

public void Delete(ref Key key) { }

public void Insert(ref Key key) { }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a variant here that takes in the address as well. In that case, we need to update the index whenever addresses changes, even if the key does not change. Like an Upsert call here in addition to Insert.


public void Upsert(ref Key key, bool isMutable) { }
}

class NullValueIndex<Value> : ISecondaryValueIndex<Value>
{
public string Name => "ValueIndex";

public bool IsMutable => true;

public void Delete(ref Value value, long recordId) { }

public void Insert(ref Value value, long recordId) { }

public void Upsert(ref Value value, long recordId, bool isMutable) { }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does isMutable mean here in the API?

}
}
4 changes: 2 additions & 2 deletions cs/src/core/Allocator/GenericAllocator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -922,7 +922,7 @@ protected override bool RetrievedFullRecord(byte* record, ref AsyncIOContext<Key
/// <returns></returns>
public override bool KeyHasObjects()
{
return SerializerSettings.keySerializer != null;
return SerializerSettings?.keySerializer != null;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check where the nullref occurred and try to fix the caller spot.

}

/// <summary>
Expand All @@ -931,7 +931,7 @@ public override bool KeyHasObjects()
/// <returns></returns>
public override bool ValueHasObjects()
{
return SerializerSettings.valueSerializer != null;
return SerializerSettings?.valueSerializer != null;
}
#endregion

Expand Down
65 changes: 61 additions & 4 deletions cs/src/core/ClientSession/AdvancedClientSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -737,15 +737,54 @@ public void ConcurrentReader(ref Key key, ref Input input, ref Value value, ref

public bool ConcurrentWriter(ref Key key, ref Value src, ref Value dst, long address)
{
return _clientSession.functions.ConcurrentWriter(ref key, ref src, ref dst, address);
if (!_clientSession.fht.SupportsMutableIndexes)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Make SupportsMutableIndexes a field of this struct and see if it helps.
  • Other thing to try is to make this struct itself readonly -- will that help.
  • Moving else path to non-inlined extra heavy-method might help the fast path?

return _clientSession.functions.ConcurrentWriter(ref key, ref src, ref dst, address);

ref RecordInfo recordInfo = ref _clientSession.fht.RecordAccessor.SpinLockRecordInfo(address);
try
{
if (!recordInfo.Tombstone && _clientSession.functions.ConcurrentWriter(ref key, ref src, ref dst, address))
{
// KeyIndexes do not need notification of in-place updates because the key does not change.
if (_clientSession.fht.SecondaryIndexBroker.MutableValueIndexCount > 0)
_clientSession.fht.SecondaryIndexBroker.Upsert(ref dst, address);
return true;
}
}
finally
{
recordInfo.Unlock();
}
return false;
}

public bool ConcurrentDeleter(ref Key key, ref Value value, long address)
{
if (!_clientSession.fht.SupportsMutableIndexes)
return _clientSession.functions.ConcurrentDeleter(ref key, ref value, address);

ref RecordInfo recordInfo = ref _clientSession.fht.RecordAccessor.SpinLockRecordInfo(address);
try
{
if (_clientSession.fht.SecondaryIndexBroker.MutableKeyIndexCount > 0)
_clientSession.fht.SecondaryIndexBroker.Delete(ref key);
if (_clientSession.fht.SecondaryIndexBroker.MutableValueIndexCount > 0)
_clientSession.fht.SecondaryIndexBroker.Delete(ref value, address);
return _clientSession.functions.ConcurrentDeleter(ref key, ref value, address);
}
finally
{
recordInfo.Unlock();
}
return true;
}

public bool NeedCopyUpdate(ref Key key, ref Input input, ref Value oldValue)
=> _clientSession.functions.NeedCopyUpdate(ref key, ref input, ref oldValue);

public void CopyUpdater(ref Key key, ref Input input, ref Value oldValue, ref Value newValue, long oldAddress, long newAddress)
public void CopyUpdater(ref Key key, ref Input input, ref Value oldValue, ref Value newValue, long address)
{
_clientSession.functions.CopyUpdater(ref key, ref input, ref oldValue, ref newValue, oldAddress, newAddress);
_clientSession.functions.CopyUpdater(ref key, ref input, ref oldValue, ref newValue, address);
}

public void DeleteCompletionCallback(ref Key key, Context ctx)
Expand All @@ -770,7 +809,25 @@ public void InitialUpdater(ref Key key, ref Input input, ref Value value, long a

public bool InPlaceUpdater(ref Key key, ref Input input, ref Value value, long address)
{
return _clientSession.functions.InPlaceUpdater(ref key, ref input, ref value, address);
if (!_clientSession.fht.SupportsMutableIndexes)
return _clientSession.functions.InPlaceUpdater(ref key, ref input, ref value, address);

ref RecordInfo recordInfo = ref _clientSession.fht.RecordAccessor.SpinLockRecordInfo(address);
try
{
if (!recordInfo.Tombstone && _clientSession.functions.InPlaceUpdater(ref key, ref input, ref value, address))
{
// KeyIndexes do not need notification of in-place updates because the key does not change.
if (_clientSession.fht.SecondaryIndexBroker.MutableValueIndexCount > 0)
_clientSession.fht.SecondaryIndexBroker.Upsert(ref value, address);
return true;
}
}
finally
{
recordInfo.Unlock();
}
return false;
}

public void ReadCompletionCallback(ref Key key, ref Input input, ref Output output, Context ctx, Status status, RecordInfo recordInfo)
Expand Down
64 changes: 61 additions & 3 deletions cs/src/core/ClientSession/ClientSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -747,13 +747,53 @@ public void ConcurrentReader(ref Key key, ref Input input, ref Value value, ref

public bool ConcurrentWriter(ref Key key, ref Value src, ref Value dst, long address)
{
return _clientSession.functions.ConcurrentWriter(ref key, ref src, ref dst);
if (!_clientSession.fht.SupportsMutableIndexes)
return _clientSession.functions.ConcurrentWriter(ref key, ref src, ref dst);

ref RecordInfo recordInfo = ref _clientSession.fht.RecordAccessor.SpinLockRecordInfo(address);
Copy link
Collaborator

@badrishc badrishc Feb 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit worrisome that locking is all over the place and using spin locks.

Scenarios to consider:

  • What if the value were 8 byte longs and so we didn't need a lock at all?
  • Or the user is okay with broken writes (value consists of individual 8-byte values that don't all need to be consistently read at ones)
  • What if we were only having key indexes, so we never need to take a lock at all (since keys are immutable)?
  • What if the user had space in key/value to have a better implementation of lock.

Would it be better if we made this a duty of (advanced) functions - capable of being overridden by specific functions. like functions.LockRecord(ref RecordInfo, ref Key, ref Value).

try
{
if (!recordInfo.Tombstone && _clientSession.functions.ConcurrentWriter(ref key, ref src, ref dst))
{
// KeyIndexes do not need notification of in-place updates because the key does not change.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lock seems wasteful to take and release if we didnt need to notify SI.

if (_clientSession.fht.SecondaryIndexBroker.MutableValueIndexCount > 0)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keys in FASTER are immutable once added to the log, so we do not need a lock when only accessing keys.

_clientSession.fht.SecondaryIndexBroker.Upsert(ref dst, address);
return true;
}
}
finally
{
recordInfo.Unlock();
}
return false;
}

public bool ConcurrentDeleter(ref Key key, ref Value value, long address)
{
// Non-Advanced IFunctions has no ConcurrentDeleter
if (!_clientSession.fht.SupportsMutableIndexes)
return false;

ref RecordInfo recordInfo = ref _clientSession.fht.RecordAccessor.SpinLockRecordInfo(address);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, we are taking a lock even when we do not need to talk to SI.

try
{
if (_clientSession.fht.SecondaryIndexBroker.MutableKeyIndexCount > 0)
_clientSession.fht.SecondaryIndexBroker.Delete(ref key);
if (_clientSession.fht.SecondaryIndexBroker.MutableValueIndexCount > 0)
_clientSession.fht.SecondaryIndexBroker.Delete(ref value, address);
_clientSession.fht.SetRecordDeleted(ref recordInfo, ref value);
}
finally
{
recordInfo.Unlock();
}
return true;
}

public bool NeedCopyUpdate(ref Key key, ref Input input, ref Value oldValue)
=> _clientSession.functions.NeedCopyUpdate(ref key, ref input, ref oldValue);

public void CopyUpdater(ref Key key, ref Input input, ref Value oldValue, ref Value newValue, long oldAddress, long newAddress)
public void CopyUpdater(ref Key key, ref Input input, ref Value oldValue, ref Value newValue, long address)
{
_clientSession.functions.CopyUpdater(ref key, ref input, ref oldValue, ref newValue);
}
Expand All @@ -780,7 +820,25 @@ public void InitialUpdater(ref Key key, ref Input input, ref Value value, long a

public bool InPlaceUpdater(ref Key key, ref Input input, ref Value value, long address)
{
return _clientSession.functions.InPlaceUpdater(ref key, ref input, ref value);
if (!_clientSession.fht.SupportsMutableIndexes)
return _clientSession.functions.InPlaceUpdater(ref key, ref input, ref value);

ref RecordInfo recordInfo = ref _clientSession.fht.RecordAccessor.SpinLockRecordInfo(address);
try
{
if (!recordInfo.Tombstone && _clientSession.functions.InPlaceUpdater(ref key, ref input, ref value))
{
// KeyIndexes do not need notification of in-place updates because the key does not change.
if (_clientSession.fht.SecondaryIndexBroker.MutableValueIndexCount > 0)
_clientSession.fht.SecondaryIndexBroker.Upsert(ref value, address);
return true;
}
}
finally
{
recordInfo.Unlock();
}
return false;
}

public void ReadCompletionCallback(ref Key key, ref Input input, ref Output output, Context ctx, Status status, RecordInfo recordInfo)
Expand Down
7 changes: 7 additions & 0 deletions cs/src/core/Index/Common/Contexts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ internal struct PendingContext<Input, Output, Context>
internal const byte kSkipReadCache = 0x01;
internal const byte kNoKey = 0x02;
internal const byte kSkipCopyReadsToTail = 0x04;
internal const byte kIsNewRecord = 0x08;

internal static byte GetOperationFlags(ReadFlags readFlags, bool noKey = false)
{
Expand Down Expand Up @@ -119,6 +120,12 @@ internal bool SkipCopyReadsToTail
set => operationFlags = value ? (byte)(operationFlags | kSkipCopyReadsToTail) : (byte)(operationFlags & ~kSkipCopyReadsToTail);
}

internal bool IsNewRecord
{
get => (operationFlags & kIsNewRecord) != 0;
set => operationFlags = value ? (byte)(operationFlags | kIsNewRecord) : (byte)(operationFlags & ~kIsNewRecord);
}

public void Dispose()
{
key?.Dispose();
Expand Down
Loading