示例#1
0
        /// <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();
            }
        }
示例#2
0
 /// <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));
 }
示例#3
0
        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));
        }
示例#4
0
 /// <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));
 }
示例#5
0
        /// <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();
            }
        }
示例#6
0
        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));
        }