private async ValueTask UnsafeAppendAsync <TEntry>(ILogEntryProducer <TEntry> supplier, long startIndex, bool skipCommitted, CancellationToken token) where TEntry : notnull, IRaftLogEntry { if (startIndex > state.LastIndex + 1) { throw new ArgumentOutOfRangeException(nameof(startIndex)); } Partition?partition; for (partition = null; !token.IsCancellationRequested && await supplier.MoveNextAsync().ConfigureAwait(false); state.LastIndex = startIndex++) { if (supplier.Current.IsSnapshot) { throw new InvalidOperationException(ExceptionMessages.SnapshotDetected); } if (startIndex > state.CommitIndex) { await GetOrCreatePartitionAsync(startIndex, ref partition).ConfigureAwait(false); await partition.WriteAsync(sessionManager.WriteSession, supplier.Current, startIndex).ConfigureAwait(false); } else if (!skipCommitted) { throw new InvalidOperationException(ExceptionMessages.InvalidAppendIndex); } } await FlushAsync(partition).ConfigureAwait(false); // flush updated state state.Flush(); token.ThrowIfCancellationRequested(); }
private async ValueTask <long> AppendAsync <TEntry>(ILogEntryProducer <TEntry> entries, long?startIndex, bool skipCommitted, CancellationToken token) where TEntry : IRaftLogEntry { if (startIndex is null) { startIndex = log.LongLength; } else if (startIndex > log.LongLength) { throw new ArgumentOutOfRangeException(nameof(startIndex)); } IRaftLogEntry[] appendingScope; if (skipCommitted) { appendingScope = await ReadAllAsync(entries, token).ConfigureAwait(false); var skipNum = Math.Max(0, GetLastIndex(true) - startIndex.Value + 1L); appendingScope = appendingScope.RemoveFirst(skipNum); startIndex += skipNum; } else if (startIndex <= GetLastIndex(true)) { throw new InvalidOperationException(ExceptionMessages.InvalidAppendIndex); } else { appendingScope = await ReadAllAsync(entries, token).ConfigureAwait(false); } Append(appendingScope, startIndex.Value); return(startIndex.Value); }
/// <inheritdoc/> async ValueTask <long> IAuditTrail <IRaftLogEntry> .AppendAsync <TEntry>(ILogEntryProducer <TEntry> entries, CancellationToken token) { if (entries.RemainingCount == 0L) { throw new ArgumentException(ExceptionMessages.EntrySetIsEmpty); } using (await syncRoot.AcquireWriteLockAsync(token).ConfigureAwait(false)) return(await AppendAsync(entries, null, false, token).ConfigureAwait(false)); }
/// <inheritdoc/> async ValueTask IAuditTrail <IRaftLogEntry> .AppendAsync <TEntryImpl>(ILogEntryProducer <TEntryImpl> entries, long startIndex, bool skipCommitted, CancellationToken token) { if (startIndex < 0L) { throw new ArgumentOutOfRangeException(nameof(startIndex)); } using (await syncRoot.AcquireWriteLockAsync(token).ConfigureAwait(false)) await AppendAsync(entries, startIndex, skipCommitted, token).ConfigureAwait(false); }
async ValueTask IAuditTrail <IRaftLogEntry> .AppendAsync <TEntry>(ILogEntryProducer <TEntry> entries, long startIndex, bool skipCommitted, CancellationToken token) { if (entries.RemainingCount == 0L) { return; } using (await syncRoot.AcquireWriteLockAsync(CancellationToken.None).ConfigureAwait(false)) await AppendAsync(entries, startIndex, skipCommitted, token); }
private async ValueTask <long> AppendAsync <TEntryImpl>(ILogEntryProducer <TEntryImpl> entries, long?startIndex, bool skipCommitted, CancellationToken token) where TEntryImpl : notnull, IRaftLogEntry { long skip; if (startIndex is null) { startIndex = index + 1L; } else if (startIndex > index + 1L) { throw new ArgumentOutOfRangeException(nameof(startIndex)); } if (skipCommitted) { skip = Math.Max(0, commitIndex.VolatileRead() - startIndex.Value + 1L); startIndex += skip; } else if (startIndex <= commitIndex.VolatileRead()) { throw new InvalidOperationException(ExceptionMessages.InvalidAppendIndex); } else { skip = 0L; } var count = entries.RemainingCount - skip; if (count > 0L) { // skip entries var newEntries = new long[count]; for (; skip-- > 0; token.ThrowIfCancellationRequested()) { await entries.MoveNextAsync().ConfigureAwait(false); } // copy terms for (var i = 0; await entries.MoveNextAsync().ConfigureAwait(false) && i < newEntries.LongLength; i++, token.ThrowIfCancellationRequested()) { if (entries.Current.IsSnapshot) { throw new InvalidOperationException(ExceptionMessages.SnapshotDetected); } newEntries[i] = entries.Current.Term; } // now concat existing array of terms Append(newEntries, startIndex.Value); } return(startIndex.Value); }
private static async ValueTask <IRaftLogEntry[]> ReadAllAsync <TEntry>(ILogEntryProducer <TEntry> entries, CancellationToken token) where TEntry : IRaftLogEntry { var bufferedEntries = new IRaftLogEntry[entries.RemainingCount]; for (var i = 0L; await entries.MoveNextAsync().ConfigureAwait(false); i++) { bufferedEntries[i] = entries.Current.IsReusable ? (IRaftLogEntry)entries.Current : await BufferedLogEntry.CreateBufferedEntryAsync(entries.Current, token).ConfigureAwait(false); } return(bufferedEntries); }
/// <inheritdoc/> async ValueTask IAuditTrail <IRaftLogEntry> .AppendAsync <TEntry>(ILogEntryProducer <TEntry> entries, long startIndex, bool skipCommitted, CancellationToken token) { if (entries.RemainingCount == 0L) { return; } await syncRoot.AcquireAsync(true, CancellationToken.None).ConfigureAwait(false); try { await UnsafeAppendAsync(entries, startIndex, skipCommitted, token).ConfigureAwait(false); } finally { syncRoot.Release(); } }
Task <Result <bool> > ILocalMember.ReceiveEntriesAsync <TEntry>(EndPoint sender, long senderTerm, ILogEntryProducer <TEntry> entries, long prevLogIndex, long prevLogTerm, long commitIndex, CancellationToken token) { var member = FindMember(MatchByEndPoint, sender); if (member is null) { return(Task.FromResult(new Result <bool>(Term, false))); } member.Touch(); return(ReceiveEntriesAsync(member, senderTerm, entries, prevLogIndex, prevLogTerm, commitIndex, token)); }
private async Task <Result <bool> > BufferizeReceivedEntriesAsync <TEntry>(RaftClusterMember sender, long senderTerm, ILogEntryProducer <TEntry> entries, long prevLogIndex, long prevLogTerm, long commitIndex, CancellationToken token) where TEntry : notnull, IRaftLogEntry { Debug.Assert(bufferingOptions is not null); using var buffered = await BufferedRaftLogEntryList.CopyAsync(entries, bufferingOptions, token).ConfigureAwait(false); return(await ReceiveEntriesAsync(sender, senderTerm, buffered.ToProducer(), prevLogIndex, prevLogTerm, commitIndex, token).ConfigureAwait(false)); }
async Task <Result <bool> > ILocalMember.ReceiveEntriesAsync <TEntry>(EndPoint sender, long senderTerm, ILogEntryProducer <TEntry> entries, long prevLogIndex, long prevLogTerm, long commitIndex, CancellationToken token) { Equal(42L, senderTerm); Equal(1, prevLogIndex); Equal(56L, prevLogTerm); Equal(10, commitIndex); byte[] buffer; switch (Behavior) { case ReceiveEntriesBehavior.ReceiveAll: while (await entries.MoveNextAsync()) { True(entries.Current.Length.HasValue); buffer = await entries.Current.ToByteArrayAsync(token); ReceivedEntries.Add(new BufferedEntry(entries.Current.Term, entries.Current.Timestamp, entries.Current.IsSnapshot, buffer)); } break; case ReceiveEntriesBehavior.DropAll: break; case ReceiveEntriesBehavior.ReceiveFirst: True(await entries.MoveNextAsync()); buffer = await entries.Current.ToByteArrayAsync(token); ReceivedEntries.Add(new BufferedEntry(entries.Current.Term, entries.Current.Timestamp, entries.Current.IsSnapshot, buffer)); break; case ReceiveEntriesBehavior.DropFirst: True(await entries.MoveNextAsync()); True(await entries.MoveNextAsync()); buffer = await entries.Current.ToByteArrayAsync(token); ReceivedEntries.Add(new BufferedEntry(entries.Current.Term, entries.Current.Timestamp, entries.Current.IsSnapshot, buffer)); break; } return(new Result <bool>(43L, true)); }