public void FasterLogShiftTailStressTest() { // Get an excruciatingly slow storage device to maximize chance of clogging the flush pipeline device = new LocalMemoryDevice(1L << 28, 1 << 28, 2, sector_size: 512, latencyMs: 50, fileName: "stress.log"); var logSettings = new FasterLogSettings { LogDevice = device, LogChecksum = LogChecksumType.None, LogCommitManager = manager, SegmentSizeBits = 28 }; log = new FasterLog(logSettings); byte[] entry = new byte[entryLength]; for (int i = 0; i < entryLength; i++) { entry[i] = (byte)i; } for (int i = 0; i < 5 * numEntries; i++) { log.Enqueue(entry); } // for comparison, insert some entries without any commit records var referenceTailLength = log.TailAddress; var enqueueDone = new ManualResetEventSlim(); var commitThreads = new List <Thread>(); // Make sure to spin up many commit threads to expose lots of interleavings for (var i = 0; i < 2 * Math.Max(1, Environment.ProcessorCount - 1); i++) { commitThreads.Add(new Thread(() => { // Otherwise, absolutely clog the commit pipeline while (!enqueueDone.IsSet) { log.Commit(); } })); } foreach (var t in commitThreads) { t.Start(); } for (int i = 0; i < 5 * numEntries; i++) { log.Enqueue(entry); } enqueueDone.Set(); foreach (var t in commitThreads) { t.Join(); } // We expect the test to finish and not get stuck somewhere // Ensure clean shutdown log.Commit(true); }
public void ScanConsumerTest([Values] TestUtils.DeviceType deviceType) { // Create log and device here (not in setup) because using DeviceType Enum which can't be used in Setup string filename = path + "LogScanDefault" + deviceType.ToString() + ".log"; device = TestUtils.CreateTestDevice(deviceType, filename); log = new FasterLog(new FasterLogSettings { LogDevice = device, SegmentSizeBits = 22, LogCommitDir = path }); PopulateLog(log); // Basic default scan from start to end // Indirectly used in other tests, but good to have the basic test here for completeness // Read the log - Look for the flag so know each entry is unique var consumer = new TestConsumer(); using (var iter = log.Scan(0, 100_000_000)) { while (iter.TryConsumeNext(consumer)) { } } // Make sure expected length is same as current - also makes sure that data verification was not skipped Assert.AreEqual(entryLength, consumer.currentEntry); }
static async Task Main() { var path = Path.GetTempPath() + "FasterLogPubSub\\"; var device = Devices.CreateLogDevice(path + "mylog"); var log = new FasterLog(new FasterLogSettings { LogDevice = device, MemorySizeBits = 11, PageSizeBits = 9, MutableFraction = 0.5, SegmentSizeBits = 9 }); using var cts = new CancellationTokenSource(); var producer = ProducerAsync(log, cts.Token); var commiter = CommiterAsync(log, cts.Token); var consumer = ConsumerAsync(log, cts.Token); Console.CancelKeyPress += (o, eventArgs) => { Console.WriteLine("Cancelling program..."); eventArgs.Cancel = true; cts.Cancel(); }; await producer; await consumer; await commiter; Console.WriteLine("Finished."); log.Dispose(); try { new DirectoryInfo(path).Delete(true); } catch { } }
public static void Main() { commitPath = "FasterLogStress/"; // Clean up log files from previous test runs in case they weren't cleaned up // We loop to ensure clean-up as deleteOnClose does not always work for MLSD while (Directory.Exists(commitPath)) { Directory.Delete(commitPath, true); } // Create devices \ log for test device = new ManagedLocalStorageDevice(commitPath + "ManagedLocalStore.log", deleteOnClose: true); log = new FasterLog(new FasterLogSettings { LogDevice = device, PageSizeBits = 12, MemorySizeBits = 14 }); ManagedLocalStoreBasicTest(); log.Dispose(); device.Dispose(); // Clean up log files if (Directory.Exists(commitPath)) { Directory.Delete(commitPath, true); } }
private void FasterLogTest1(LogChecksumType logChecksum, IDevice device, ILogCommitManager logCommitManager) { log = new FasterLog(new FasterLogSettings { PageSizeBits = 20, SegmentSizeBits = 20, LogDevice = device, LogChecksum = logChecksum, LogCommitManager = logCommitManager }); byte[] entry = new byte[entryLength]; for (int i = 0; i < entryLength; i++) { entry[i] = (byte)i; } for (int i = 0; i < numEntries; i++) { log.Enqueue(entry); } log.Commit(true); using (var iter = log.Scan(0, long.MaxValue)) { int count = 0; while (iter.GetNext(out byte[] result, out int length, out long currentAddress)) { count++; Assert.IsTrue(result.SequenceEqual(entry)); if (count % 100 == 0) { log.TruncateUntil(iter.NextAddress); } } Assert.IsTrue(count == numEntries); } log.Dispose(); }
static async Task Main() { var device = Devices.CreateLogDevice(path + "mylog"); var log = new FasterLog(new FasterLogSettings { LogDevice = device, MemorySizeBits = 11, PageSizeBits = 9, MutableFraction = 0.5, SegmentSizeBits = 9 }); using var cts = new CancellationTokenSource(); var producer = ProducerAsync(log, cts.Token); var commiter = CommitterAsync(log, cts.Token); // Consumer on SAME FasterLog instance var consumer = ConsumerAsync(log, true, cts.Token); // Uncomment below to run consumer on SEPARATE read-only FasterLog instance // var consumer = SeparateConsumerAsync(cts.Token); Console.CancelKeyPress += (o, eventArgs) => { Console.WriteLine("Cancelling program..."); eventArgs.Cancel = true; cts.Cancel(); }; await producer; await consumer; await commiter; Console.WriteLine("Finished."); log.Dispose(); try { new DirectoryInfo(path).Delete(true); } catch { } }
static async Task Main() { var device = Devices.CreateLogDevice($"c:\\logs\\mylog"); var log = new FasterLog(new FasterLogSettings { LogDevice = device, MemorySizeBits = 11, PageSizeBits = 9, MutableFraction = 0.5, SegmentSizeBits = 9 }); using var cts = new CancellationTokenSource(); var producer = ProducerAsync(log, cts.Token); var commiter = CommiterAsync(log, cts.Token); var consumer = ConsumerAsync(log, cts.Token); Console.CancelKeyPress += (o, eventArgs) => { Console.WriteLine("Cancelling program..."); eventArgs.Cancel = true; cts.Cancel(); }; await producer; await consumer; await commiter; Console.WriteLine("Finished."); }
public async Task FasterLogResumePersistedReaderSpec([Values] LogChecksumType logChecksum) { var input1 = new byte[] { 0, 1, 2, 3 }; var input2 = new byte[] { 4, 5, 6, 7, 8, 9, 10 }; var input3 = new byte[] { 11, 12 }; string readerName = "abc"; using (var l = new FasterLog(new FasterLogSettings { LogDevice = device, PageSizeBits = 16, MemorySizeBits = 16, LogChecksum = logChecksum, LogCommitFile = commitPath })) { await l.EnqueueAsync(input1); await l.EnqueueAsync(input2); await l.EnqueueAsync(input3); await l.CommitAsync(); using var originalIterator = l.Scan(0, long.MaxValue, readerName); Assert.IsTrue(originalIterator.GetNext(out _, out _, out _, out long recoveryAddress)); originalIterator.CompleteUntil(recoveryAddress); Assert.IsTrue(originalIterator.GetNext(out _, out _, out _, out _)); // move the reader ahead await l.CommitAsync(); } using (var l = new FasterLog(new FasterLogSettings { LogDevice = device, PageSizeBits = 16, MemorySizeBits = 16, LogChecksum = logChecksum, LogCommitFile = commitPath })) { using var recoveredIterator = l.Scan(0, long.MaxValue, readerName); Assert.IsTrue(recoveredIterator.GetNext(out byte[] outBuf, out _, out _, out _)); Assert.True(input2.SequenceEqual(outBuf)); // we should have read in input2, not input1 or input3 } }
public void TestDisposeReleasesFileLocksWithCompletedCommit([Values] TestUtils.DeviceType deviceType) { string path = TestUtils.MethodTestDir + "/"; string filename = path + "TestDisposeRelease" + deviceType.ToString() + ".log"; DirectoryInfo di = Directory.CreateDirectory(path); IDevice device = TestUtils.CreateTestDevice(deviceType, filename); FasterLog fasterLog = new FasterLog(new FasterLogSettings { LogDevice = device, SegmentSizeBits = 22, LogCommitDir = path, LogChecksum = LogChecksumType.PerEntry }); Assert.IsTrue(fasterLog.TryEnqueue(new byte[100], out _)); fasterLog.Commit(spinWait: true); fasterLog.Dispose(); device.Dispose(); while (true) { try { di.Delete(recursive: true); break; } catch { } } }
public void FasterLogTest1([Values] LogChecksumType logChecksum) { log = new FasterLog(new FasterLogSettings { LogDevice = device, LogChecksum = logChecksum }); byte[] entry = new byte[entryLength]; for (int i = 0; i < entryLength; i++) { entry[i] = (byte)i; } for (int i = 0; i < numEntries; i++) { log.Enqueue(entry); } log.Commit(true); using (var iter = log.Scan(0, long.MaxValue)) { int count = 0; while (iter.GetNext(out byte[] result, out int length)) { count++; Assert.IsTrue(result.SequenceEqual(entry)); if (count % 100 == 0) { log.TruncateUntil(iter.CurrentAddress); } } Assert.IsTrue(count == numEntries); } log.Dispose(); }
public void PopulateLog(FasterLog log) { //****** Populate log for Basic data for tests // Set Default entry data for (int i = 0; i < entryLength; i++) { entry[i] = (byte)i; } // Enqueue but set each Entry in a way that can differentiate between entries for (int i = 0; i < numEntries; i++) { // Flag one part of entry data that corresponds to index if (i < entryLength) { entry[i] = (byte)entryFlag; } // puts back the previous entry value if ((i > 0) && (i < entryLength)) { entry[i - 1] = (byte)(i - 1); } // Add to FasterLog log.Enqueue(entry); } // Commit to the log log.Commit(true); }
public void PopulateUncommittedLog(FasterLog logUncommitted) { //****** Populate uncommitted log / device for ScanUncommittedTest // Set Default entry data for (int j = 0; j < entryLength; j++) { entry[j] = (byte)j; } // Enqueue but set each Entry in a way that can differentiate between entries for (int j = 0; j < numEntries; j++) { // Flag one part of entry data that corresponds to index if (j < entryLength) { entry[j] = (byte)entryFlag; } // puts back the previous entry value if ((j > 0) && (j < entryLength)) { entry[j - 1] = (byte)(j - 1); } // Add to FasterLog logUncommitted.Enqueue(entry); } // refresh uncommitted so can see it when scan - do NOT commit though logUncommitted.RefreshUncommitted(true); }
public void ScanWithoutRecoverTest([Values] TestUtils.DeviceType deviceType) { // You may also force an iterator to start at the specified begin address, i.e., without recovering: recover parameter = false // Create log and device here (not in setup) because using DeviceType Enum which can't be used in Setup string filename = path + "LogScanWithoutRecover" + deviceType.ToString() + ".log"; device = TestUtils.CreateTestDevice(deviceType, filename); log = new FasterLog(new FasterLogSettings { LogDevice = device, SegmentSizeBits = 22, LogCommitDir = path }); PopulateLog(log); // Read the log int currentEntry = 9; // since starting at specified address of 1000, need to set current entry as 9 so verification starts at proper spot using (var iter = log.Scan(1000, 100_000_000, recover: false)) { while (iter.GetNext(out byte[] result, out _, out _)) { if (currentEntry < entryLength) { // Span Batch only added first entry several times so have separate verification Assert.AreEqual((byte)entryFlag, result[currentEntry]); currentEntry++; } } } // Make sure expected length is same as current - also makes sure that data verification was not skipped Assert.AreEqual(entryLength, currentEntry); }
public void ScanUncommittedTest([Values] TestUtils.DeviceType deviceType) { // Create log and device here (not in setup) because using DeviceType Enum which can't be used in Setup string filename = path + "LogScan" + deviceType.ToString() + ".log"; device = TestUtils.CreateTestDevice(deviceType, filename); log = new FasterLog(new FasterLogSettings { LogDevice = device, SegmentSizeBits = 22, LogCommitDir = path }); PopulateUncommittedLog(log); // Setting scanUnCommitted to true is actual test here. // Read the log - Look for the flag so know each entry is unique and still reads uncommitted int currentEntry = 0; using (var iter = log.Scan(0, 100_000_000, scanUncommitted: true)) { while (iter.GetNext(out byte[] result, out _, out _)) { if (currentEntry < entryLength) { // Span Batch only added first entry several times so have separate verification Assert.AreEqual((byte)entryFlag, result[currentEntry]); currentEntry++; } } } // Make sure expected length is same as current - also makes sure that data verification was not skipped Assert.AreEqual(entryLength, currentEntry); }
public void ScanByNameTest([Values] TestUtils.DeviceType deviceType) { //You can persist iterators(or more precisely, their CompletedUntilAddress) as part of a commit by simply naming them during their creation. // Create log and device here (not in setup) because using DeviceType Enum which can't be used in Setup string filename = path + "LogScanByName" + deviceType.ToString() + ".log"; device = TestUtils.CreateTestDevice(deviceType, filename); log = new FasterLog(new FasterLogSettings { LogDevice = device, SegmentSizeBits = 22, LogCommitDir = path }); PopulateLog(log); // Read the log - Look for the flag so know each entry is unique int currentEntry = 0; using (var iter = log.Scan(0, 100_000_000, name: "TestScan", recover: true)) { while (iter.GetNext(out byte[] result, out _, out _)) { if (currentEntry < entryLength) { // Span Batch only added first entry several times so have separate verification Assert.AreEqual((byte)entryFlag, result[currentEntry]); currentEntry++; } } } // Make sure expected length is same as current - also makes sure that data verification was not skipped Assert.AreEqual(entryLength, currentEntry); }
public void BasicHighLatencyDeviceTest() { TestUtils.DeleteDirectory(TestUtils.MethodTestDir, wait: true); // Create devices \ log for test for in memory device using LocalMemoryDevice device = new LocalMemoryDevice(1L << 28, 1L << 25, 2, latencyMs: 20); using FasterLog LocalMemorylog = new FasterLog(new FasterLogSettings { LogDevice = device, PageSizeBits = 80, MemorySizeBits = 20, GetMemory = null, SegmentSizeBits = 80, MutableFraction = 0.2, LogCommitManager = null }); int entryLength = 10; // Set Default entry data for (int i = 0; i < entryLength; i++) { entry[i] = (byte)i; LocalMemorylog.Enqueue(entry); } // Commit to the log LocalMemorylog.Commit(true); // Read the log just to verify was actually committed int currentEntry = 0; using (var iter = LocalMemorylog.Scan(0, 100_000_000)) { while (iter.GetNext(out byte[] result, out _, out _)) { Assert.IsTrue(result[currentEntry] == currentEntry, "Fail - Result[" + currentEntry.ToString() + "]: is not same as " + currentEntry.ToString()); currentEntry++; } } }
public void FasterLogTest6([Values] LogChecksumType logChecksum) { log = new FasterLog(new FasterLogSettings { LogDevice = device, MemorySizeBits = 20, PageSizeBits = 14, LogChecksum = logChecksum, LogCommitManager = manager }); byte[] data1 = new byte[1000]; for (int i = 0; i < 100; i++) { data1[i] = (byte)i; } for (int i = 0; i < 100; i++) { log.Enqueue(data1); } log.RefreshUncommitted(); Assert.IsTrue(log.SafeTailAddress == log.TailAddress); Assert.IsTrue(log.CommittedUntilAddress < log.SafeTailAddress); using (var iter = log.Scan(0, long.MaxValue, scanUncommitted: true)) { while (iter.GetNext(out _, out _, out _)) { log.TruncateUntil(iter.NextAddress); } Assert.IsTrue(iter.NextAddress == log.SafeTailAddress); log.Enqueue(data1); Assert.IsFalse(iter.GetNext(out _, out _, out _)); log.RefreshUncommitted(); Assert.IsTrue(iter.GetNext(out _, out _, out _)); } log.Dispose(); }
static void LogWriter(FasterLog log, byte[] entry) { // Enter in some entries then wait on this separate thread log.Enqueue(entry); log.Enqueue(entry); log.Enqueue(entry); log.WaitForCommit(log.TailAddress); }
private FasterOps() { string devicePath = Startup.Configuration[nameof(MinMQConfiguration.FasterDevice)]; device = Devices.CreateLogDevice(devicePath); logger = new FasterLog(new FasterLogSettings { LogDevice = device, }); }
//**** Helper Functions - based off of FasterLogPubSub sample *** static async Task CommitterAsync(FasterLog log, CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { await Task.Delay(TimeSpan.FromMilliseconds(commitPeriodMs), cancellationToken); await log.CommitAsync(token : cancellationToken); } }
public async ValueTask FlakyLogTestCleanFailure([Values] bool isAsync) { var errorOptions = new ErrorSimulationOptions { readTransientErrorRate = 0, readPermanentErrorRate = 0, writeTransientErrorRate = 0, writePermanentErrorRate = 0.1, }; device = new SimulatedFlakyDevice(Devices.CreateLogDevice(path + "fasterlog.log", deleteOnClose: true), errorOptions); var logSettings = new FasterLogSettings { LogDevice = device, LogChecksum = LogChecksumType.PerEntry, LogCommitManager = manager }; log = new FasterLog(logSettings); byte[] entry = new byte[entryLength]; for (int i = 0; i < entryLength; i++) { entry[i] = (byte)i; } try { // Ensure we execute long enough to trigger errors for (int j = 0; j < 100; j++) { for (int i = 0; i < numEntries; i++) { log.Enqueue(entry); } if (isAsync) { await log.CommitAsync(); } else { log.Commit(); } } } catch (CommitFailureException e) { var errorRangeStart = e.LinkedCommitInfo.CommitInfo.FromAddress; Assert.LessOrEqual(log.CommittedUntilAddress, errorRangeStart); Assert.LessOrEqual(log.FlushedUntilAddress, errorRangeStart); return; } // Should not ignore failures Assert.Fail(); }
static async Task BeginRecoverReadOnlyLoop(FasterLog log, CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { // Delay for a while before checking again. await Task.Delay(TimeSpan.FromMilliseconds(restorePeriodMs), cancellationToken); await log.RecoverReadOnlyAsync(cancellationToken); } }
public async Task InitializeAsync() { if (!System.IO.Directory.Exists(this._BaseFolder)) { System.IO.Directory.CreateDirectory(this._BaseFolder); } FasterLogSettings logSettings = new FasterLogSettings(); this._Log = await FasterLog.CreateAsync(logSettings); }
public void TearDown() { log?.Dispose(); log = null; device?.Dispose(); device = null; // Clean up log files TestUtils.DeleteDirectory(path, wait: true); }
public void TearDown() { log?.Dispose(); log = null; device?.Dispose(); device = null; // Clean up log files TestUtils.DeleteDirectory(TestUtils.MethodTestDir); }
/// <summary> /// Main program entry point /// </summary> static void Main() { bool sync = true; var device = Devices.CreateLogDevice("D:\\logs\\hlog.log"); log = new FasterLog(new FasterLogSettings { LogDevice = device }); // Populate entry being inserted for (int i = 0; i < entryLength; i++) { staticEntry[i] = (byte)i; } if (sync) { // Log writer thread: create as many as needed new Thread(new ThreadStart(LogWriterThread)).Start(); // Threads for scan, reporting, commit new Thread(new ThreadStart(ScanThread)).Start(); new Thread(new ThreadStart(ReportThread)).Start(); new Thread(new ThreadStart(CommitThread)).Start(); } else { // Async version of demo: expect lower performance // particularly for small payload sizes const int NumParallelTasks = 10_000; ThreadPool.SetMinThreads(2 * Environment.ProcessorCount, 2 * Environment.ProcessorCount); TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs e) => { Console.WriteLine($"Unobserved task exception: {e.Exception}"); e.SetObserved(); }; Task[] tasks = new Task[NumParallelTasks]; for (int i = 0; i < NumParallelTasks; i++) { int local = i; tasks[i] = Task.Run(() => AsyncLogWriter(local)); } var scan = Task.Run(() => AsyncScan()); // Threads for reporting, commit new Thread(new ThreadStart(ReportThread)).Start(); new Thread(new ThreadStart(CommitThread)).Start(); Task.WaitAll(tasks); Task.WaitAll(scan); } }
public FasterEventEntitySaver() { var filename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "testData.mydata"); var device = Devices.CreateLogDevice(filename); _log = new FasterLog(new FasterLogSettings { LogDevice = device, }); }
static async Task Main() { // This is two samples in one, enumerating over the same FasterLog instance that does commits, or over a separate // FasterLog that opens the log file read-only and continuously catches up with the first intance's commits. const bool sameInstance = true; #pragma warning disable CS0162 // Unreachable code detected if (!sameInstance) { // Because the SAME-instance iterator illustrates truncating the log, the SEPARATE-instance may encounter EOF // issues if it is run after that truncation without cleaning up the directory first. // In all other cases, the sample should run without needing to clean up the directory. //if (Directory.Exists(path)) Directory.Delete(path, true); } var device = Devices.CreateLogDevice(path + "mylog"); var log = new FasterLog(new FasterLogSettings { LogDevice = device, MemorySizeBits = 11, PageSizeBits = 9, MutableFraction = 0.5, SegmentSizeBits = 9 }); using var cts = new CancellationTokenSource(); var producer = ProducerAsync(log, cts.Token); var committer = CommitterAsync(log, cts.Token); Task consumer; if (sameInstance) { // Consumer on SAME FasterLog instance consumer = ConsumerAsync(log, true, cts.Token); } else { // Consumer on SEPARATE read-only FasterLog instance consumer = SeparateConsumerAsync(cts.Token); } #pragma warning restore CS0162 // Unreachable code detected Console.CancelKeyPress += (o, eventArgs) => { Console.WriteLine("Cancelling program..."); eventArgs.Cancel = true; cts.Cancel(); }; await producer; await consumer; await committer; Console.WriteLine("Finished."); log.Dispose(); device.Dispose(); try { new DirectoryInfo(path).Delete(true); } catch { } }
static async Task CommiterAsync(FasterLog log, CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { await Task.Delay(TimeSpan.FromMilliseconds(5000), cancellationToken); Console.WriteLine("Committing..."); await log.CommitAsync(); } }
public async Task FasterLogResumePersistedReader2([Values] LogChecksumType logChecksum, [Values] bool overwriteLogCommits, [Values] bool removeOutdated) { var input1 = new byte[] { 0, 1, 2, 3 }; var input2 = new byte[] { 4, 5, 6, 7, 8, 9, 10 }; var input3 = new byte[] { 11, 12 }; string readerName = "abc"; using (var logCommitManager = new DeviceLogCommitCheckpointManager(new LocalStorageNamedDeviceFactory(), new DefaultCheckpointNamingScheme(commitPath), overwriteLogCommits, removeOutdated)) { long originalCompleted; using (var l = new FasterLog(new FasterLogSettings { LogDevice = device, PageSizeBits = 16, MemorySizeBits = 16, LogChecksum = logChecksum, LogCommitManager = logCommitManager })) { await l.EnqueueAsync(input1); await l.CommitAsync(); await l.EnqueueAsync(input2); await l.CommitAsync(); await l.EnqueueAsync(input3); await l.CommitAsync(); using (var originalIterator = l.Scan(0, long.MaxValue, readerName)) { originalIterator.GetNext(out _, out _, out _, out long recoveryAddress); originalIterator.CompleteUntil(recoveryAddress); originalIterator.GetNext(out _, out _, out _, out _); // move the reader ahead await l.CommitAsync(); originalCompleted = originalIterator.CompletedUntilAddress; } } using (var l = new FasterLog(new FasterLogSettings { LogDevice = device, PageSizeBits = 16, MemorySizeBits = 16, LogChecksum = logChecksum, LogCommitManager = logCommitManager })) { using (var recoveredIterator = l.Scan(0, long.MaxValue, readerName)) { recoveredIterator.GetNext(out byte[] outBuf, out _, out _, out _); // we should have read in input2, not input1 or input3 Assert.True(input2.SequenceEqual(outBuf), $"Original: {input2[0]}, Recovered: {outBuf[0]}, Original: {originalCompleted}, Recovered: {recoveredIterator.CompletedUntilAddress}"); // TestContext.Progress.WriteLine($"Original: {originalCompleted}, Recovered: {recoveredIterator.CompletedUntilAddress}"); } } } }