/// <summary> /// Gets log entries in the specified range. /// </summary> /// <remarks> /// This method may return less entries than <c>endIndex - startIndex + 1</c>. It may happen if the requested entries are committed entries and squashed into the single entry called snapshot. /// In this case the first entry in the collection is a snapshot entry. Additionally, the caller must call <see cref="IDisposable.Dispose"/> to release resources associated /// with the audit trail segment with entries. /// </remarks> /// <typeparam name="TResult">The type of the result.</typeparam> /// <param name="reader">The reader of the log entries.</param> /// <param name="startIndex">The index of the first requested log entry, inclusively.</param> /// <param name="endIndex">The index of the last requested log entry, inclusively.</param> /// <param name="token">The token that can be used to cancel the operation.</param> /// <returns>The collection of log entries.</returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="startIndex"/> or <paramref name="endIndex"/> is negative.</exception> /// <exception cref="IndexOutOfRangeException"><paramref name="endIndex"/> is greater than the index of the last added entry.</exception> public async ValueTask <TResult> ReadAsync <TResult>(LogEntryConsumer <IRaftLogEntry, TResult> reader, long startIndex, long endIndex, CancellationToken token) { if (startIndex < 0L) { throw new ArgumentOutOfRangeException(nameof(startIndex)); } if (endIndex < 0L) { throw new ArgumentOutOfRangeException(nameof(endIndex)); } if (endIndex < startIndex) { return(await reader.ReadAsync <LogEntry, LogEntry[]>(Array.Empty <LogEntry>(), null, token).ConfigureAwait(false)); } // obtain weak lock as read lock await syncRoot.AcquireAsync(false, token).ConfigureAwait(false); var session = sessionManager.OpenSession(bufferSize); try { return(await ReadAsync(reader, session, startIndex, endIndex, token).ConfigureAwait(false)); } finally { sessionManager.CloseSession(session); // return session back to the pool syncRoot.Release(); } }
/// <summary> /// Reads the log entries starting at the specified index to the end of the log. /// </summary> /// <typeparam name="TResult">The type of the result.</typeparam> /// <param name="reader">The reader over log entries.</param> /// <param name="startIndex">The index of the first log entry to retrieve.</param> /// <param name="token">The token that can be used to cancel the operation.</param> /// <returns>The result of the transformation applied to the range of the log entries.</returns> public async ValueTask <TResult> ReadAsync <TResult>(LogEntryConsumer <IRaftLogEntry, TResult> reader, long startIndex, CancellationToken token) { if (startIndex < 0L) { throw new ArgumentOutOfRangeException(nameof(startIndex)); } using (await syncRoot.AcquireReadLockAsync(token).ConfigureAwait(false)) return(await ReadCoreAsync(reader, startIndex, index.VolatileRead(), token).ConfigureAwait(false)); }
private ValueTask <TResult> ReadCoreAsync <TResult>(LogEntryConsumer <IRaftLogEntry, TResult> reader, long startIndex, long endIndex, CancellationToken token) { if (endIndex > index.VolatileRead()) { throw new ArgumentOutOfRangeException(nameof(endIndex)); } var commitIndex = this.commitIndex.VolatileRead(); var offset = startIndex - commitIndex - 1L; return(reader.ReadAsync <EmptyLogEntry, EntryList>(new EntryList(log, endIndex - startIndex + 1, offset, lastTerm.VolatileRead()), offset >= 0 ? null : new long?(commitIndex), token)); }
/// <summary> /// Reads the range of the log entries from this storage. /// </summary> /// <typeparam name="TResult">The type of the result.</typeparam> /// <param name="reader">The reader over log entries.</param> /// <param name="startIndex">The index of the first log entry to retrieve.</param> /// <param name="endIndex">The index of the last log entry to retrieve, inclusive.</param> /// <param name="token">The token that can be used to cancel the operation.</param> /// <returns>The result of the transformation applied to the range of the log entries.</returns> public async ValueTask <TResult> ReadAsync <TResult>(LogEntryConsumer <IRaftLogEntry, TResult> reader, long startIndex, long endIndex, CancellationToken token) { if (startIndex < 0L) { throw new ArgumentOutOfRangeException(nameof(startIndex)); } if (endIndex < 0L) { throw new ArgumentOutOfRangeException(nameof(endIndex)); } if (endIndex < startIndex) { return(await reader.ReadAsync <EmptyLogEntry, EmptyLogEntry[]>(Array.Empty <EmptyLogEntry>(), null, token).ConfigureAwait(false)); } using (await syncRoot.AcquireReadLockAsync(token).ConfigureAwait(false)) return(await ReadCoreAsync(reader, startIndex, endIndex, token).ConfigureAwait(false)); }
/// <summary> /// Gets log entries starting from the specified index to the last log entry. /// </summary> /// <typeparam name="TResult">The type of the result.</typeparam> /// <param name="reader">The reader of the log entries.</param> /// <param name="startIndex">The index of the first requested log entry, inclusively.</param> /// <param name="token">The token that can be used to cancel the operation.</param> /// <returns>The collection of log entries.</returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="startIndex"/> is negative.</exception> public async ValueTask <TResult> ReadAsync <TResult>(LogEntryConsumer <IRaftLogEntry, TResult> reader, long startIndex, CancellationToken token) { if (startIndex < 0L) { throw new ArgumentOutOfRangeException(nameof(startIndex)); } await syncRoot.AcquireAsync(false, token).ConfigureAwait(false); var session = sessionManager.OpenSession(bufferSize); try { return(await ReadAsync(reader, session, startIndex, state.LastIndex, token).ConfigureAwait(false)); } finally { sessionManager.CloseSession(session); syncRoot.Release(); } }
private async ValueTask <TResult> ReadAsync <TResult>(LogEntryConsumer <IRaftLogEntry, TResult> reader, DataAccessSession session, long startIndex, long endIndex, CancellationToken token) { if (startIndex > state.LastIndex) { throw new IndexOutOfRangeException(ExceptionMessages.InvalidEntryIndex(endIndex)); } if (endIndex > state.LastIndex) { throw new IndexOutOfRangeException(ExceptionMessages.InvalidEntryIndex(endIndex)); } var length = endIndex - startIndex + 1L; if (length > int.MaxValue) { throw new InternalBufferOverflowException(ExceptionMessages.RangeTooBig); } LogEntry entry; ValueTask <TResult> result; if (partitionTable.Count > 0) { using var list = entryPool.Invoke((int)length, true); var listIndex = 0; for (Partition?partition = null; startIndex <= endIndex; list[listIndex++] = entry, startIndex++) { if (startIndex > 0L && TryGetPartition(startIndex, ref partition, out var switched)) { // handle regular record entry = await partition.ReadAsync(session, startIndex, true, switched, token).ConfigureAwait(false); } else if (snapshot.Length > 0 && startIndex <= state.CommitIndex) { // probably the record is snapshotted entry = await snapshot.ReadAsync(session, token).ConfigureAwait(false); // skip squashed log entries startIndex = SquashedIndex; } else { Debug.Assert(startIndex == 0L); // handle ephemeral entity entry = initialEntry; } } return(await reader.ReadAsync <LogEntry, InMemoryList <LogEntry> >(list.Memory.Slice(0, listIndex), list[0].SnapshotIndex, token).ConfigureAwait(false)); } else if (snapshot.Length > 0) { entry = await snapshot.ReadAsync(session, token).ConfigureAwait(false); result = reader.ReadAsync <LogEntry, SingletonEntryList <LogEntry> >(new SingletonEntryList <LogEntry>(entry), entry.SnapshotIndex, token); } else { result = startIndex == 0L ? reader.ReadAsync <LogEntry, SingletonEntryList <LogEntry> >(new SingletonEntryList <LogEntry>(initialEntry), null, token) : reader.ReadAsync <LogEntry, LogEntry[]>(Array.Empty <LogEntry>(), null, token); } return(await result.ConfigureAwait(false)); }