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

First implementation of new SI API #2

Open
wants to merge 48 commits into
base: master
Choose a base branch
from
Open

Conversation

TedHartMS
Copy link
Owner

No description provided.

@TedHartMS TedHartMS requested a review from badrishc February 17, 2021 22:57

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 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?

@@ -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.

@@ -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?

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).

{
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.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.

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Insert(ref TKVKey key)
{
foreach (var keyIndex in mutableKeyIndexes)
Copy link
Collaborator

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)
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.

{
this.locking = locking;
}
public SpanByteFunctions(bool locking = false) : base(locking) { }
Copy link
Collaborator

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)
Copy link
Collaborator

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.

@@ -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>
Copy link
Collaborator

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)
Copy link
Collaborator

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?

UPSERT,
/// <summary>Insert operation (either Upsert or RMW resulted in a new record)</summary>
Copy link
Collaborator

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);
Copy link
Collaborator

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.

Copy link
Collaborator

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?

TedHartMS and others added 9 commits February 24, 2021 21:51
- 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
TedHartMS added 30 commits March 8, 2021 21:09
…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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants