-
Notifications
You must be signed in to change notification settings - Fork 0
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
First implementation of new SI API #2
base: master
Are you sure you want to change the base?
Conversation
cs/benchmark/SecondaryIndexes.cs
Outdated
|
||
public void Delete(ref Key key) { } | ||
|
||
public void Insert(ref Key key) { } |
There was a problem hiding this comment.
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.
cs/benchmark/SecondaryIndexes.cs
Outdated
|
||
public void Insert(ref Value value, long recordId) { } | ||
|
||
public void Upsert(ref Value value, long recordId, bool isMutable) { } |
There was a problem hiding this comment.
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?
@@ -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; |
There was a problem hiding this comment.
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.
@@ -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) |
There was a problem hiding this comment.
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?
if (!_clientSession.fht.SupportsMutableIndexes) | ||
return _clientSession.functions.ConcurrentWriter(ref key, ref src, ref dst); | ||
|
||
ref RecordInfo recordInfo = ref _clientSession.fht.RecordAccessor.SpinLockRecordInfo(address); |
There was a problem hiding this comment.
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)
.
{ | ||
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. |
There was a problem hiding this comment.
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.SupportsMutableIndexes) | ||
return false; | ||
|
||
ref RecordInfo recordInfo = ref _clientSession.fht.RecordAccessor.SpinLockRecordInfo(address); |
There was a problem hiding this comment.
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.
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
public void Insert(ref TKVKey key) | ||
{ | ||
foreach (var keyIndex in mutableKeyIndexes) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this iteration thread-safe with adding indexes?
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. | ||
if (_clientSession.fht.SecondaryIndexBroker.MutableValueIndexCount > 0) |
There was a problem hiding this comment.
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.
{ | ||
this.locking = locking; | ||
} | ||
public SpanByteFunctions(bool locking = false) : base(locking) { } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we still doing locking inside ConcurrentWriter below? Shouldn't it be refactored into Lock() and Unlock() overrides.
|
||
/// <summary> | ||
/// Constructor | ||
/// </summary> | ||
/// <param name="memoryPool"></param> | ||
/// <param name="locking">Whether we lock values before concurrent operations (implemented using a spin lock on length header bit)</param> | ||
public MemoryFunctions(MemoryPool<T> memoryPool = default, bool locking = false) | ||
public MemoryFunctions(MemoryPool<T> memoryPool = default, bool locking = false) : base(locking) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we still doing locking inside ConcurrentWriter below? Shouldn't it be refactored into Lock() and Unlock() overrides.
cs/test/ObjectTestTypes.cs
Outdated
@@ -328,13 +281,13 @@ public class MyLargeOutput | |||
public MyLargeValue value; | |||
} | |||
|
|||
public class MyLargeFunctions : IFunctions<MyKey, MyLargeValue, MyInput, MyLargeOutput, Empty> | |||
public class MyLargeFunctions : FunctionsBase<MyKey, MyLargeValue, MyInput, MyLargeOutput, Empty> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be good practice to seal
all overridden user functions.
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
public void Delete(long recordId) | ||
{ | ||
foreach (var valueIndex in mutableValueIndexes) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if foreach
as efficient in assembly code as a normal for loop?
cs/src/core/Index/Common/Contexts.cs
Outdated
UPSERT, | ||
/// <summary>Insert operation (either Upsert or RMW resulted in a new record)</summary> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is INSERT also used in internal use of OperationType
. Why not keep a separate enum for the user facing API compared to internal?
{ | ||
return _clientSession.functions.InPlaceUpdater(ref key, ref input, ref value, address); | ||
long context = 0; | ||
this.Lock(ref recordInfo, ref key, ref value, OperationType.RMW, ref context); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems overkill to send OperationType to Lock. All we need to know here is whether this is a reader lock or writer lock. That is why I suggested either a new enum OpType
or just a bool.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe call it LockType.Shared and Exclusive?
- enable locking in YCSB benchmark - re-allow threadAffinitized sessions with indexes (still need to add some SI calls in pending handlers) - rename to .SupportsLocking - create LockType enum rather than reusing OperationType - make tests for context.IsNewRecord more efficient in ContextUpsert et al.
… add an iteration count; refactor Memory and Span Functions to use the new locking scheme
…files, validate params, generate parsable output
- Force synthetic data for kUseSmallData - Add the Zipf generator for synthetic data - Improve output formatting so it is more easily parsable and has all needed info
…Id from Key Index and split out the Key vs. Value index test base classes; make IHeapContainer inherit from IDisposable; add SI calls to UpsertAsync
…nternal; Fix QueryRecord.Dispose(); make RecordId.IsDefault() a method so it's not serialized
- Add RIDs to Key indexes - Add TKVKEey to Value indexes - Add Checkpoint/Restore API - Add Truncation API
No description provided.