Пример #1
0
        public void CannotCreateMoreThanOneWriter()
        {
            var logDirectoryPath = GetLogDirectoryPath();
            var log    = new LogDirectory(logDirectoryPath, new Settings(writeLockAcquisitionTimeoutSeconds: 3));
            var writer = log.GetWriter();

            Using(writer);

            var exception = Assert.Throws <TimeoutException>(() => log.GetWriter());

            Console.WriteLine(exception);
        }
Пример #2
0
        public async Task CompareSingleVersusMany(bool single, int count)
        {
            SetLogLevel(LogEventLevel.Information);

            var messages     = Enumerable.Range(0, count).Select(n => $"THIS IS A STRING MESSAGE EVENT/{n}").ToList();
            var logDirectory = new LogDirectory(GetLogDirectoryPath());

            var stopwatch = Stopwatch.StartNew();

            using (var writer = logDirectory.GetWriter())
            {
                Log.Information("Writing");
                if (single)
                {
                    foreach (var message in messages)
                    {
                        await writer.WriteAsync(Encoding.UTF8.GetBytes(message));
                    }
                }
                else
                {
                    await writer.WriteManyAsync(messages.Select(Encoding.UTF8.GetBytes));
                }
                Log.Information("Done writing!");

                await Task.Delay(TimeSpan.FromSeconds(0.1));
            }

            var elapsedSeconds = stopwatch.Elapsed.TotalSeconds;

            Console.WriteLine($"Wrote {count} msgs in {elapsedSeconds:0.0} s - that's {count / elapsedSeconds:0.0} msg/s");
        }
Пример #3
0
        public async Task CheckResumption(int count)
        {
            var directoryPath = GetLogDirectoryPath();
            var logDirectory  = new LogDirectory(directoryPath);

            using (var writer = logDirectory.GetWriter())
            {
                var data = Enumerable.Range(0, count)
                           .Select(n => $"THIS IS LINE NUMBER {n}")
                           .Select(Encoding.UTF8.GetBytes);

                await writer.WriteManyAsync(data);
            }

            new DirectoryInfo(directoryPath).DumpDirectoryContentsToConsole();

            // read events in a very inefficient way, checking that we can resume at each single line
            var fileNumber   = -1;
            var bytePosition = -1;

            for (var counter = 0; counter < count; counter++)
            {
                var expectedText = $"THIS IS LINE NUMBER {counter}";
                var eventData    = logDirectory.GetReader().Read(fileNumber, bytePosition).FirstOrDefault();

                var actualText = Encoding.UTF8.GetString(eventData.Data);

                Assert.That(actualText, Is.EqualTo(expectedText));

                fileNumber   = eventData.FileNumber;
                bytePosition = eventData.BytePosition;
            }
        }
        public async Task CanCreateFilesOfDifferentSize(int approxFileLength)
        {
            var logDirectoryPath = GetLogDirectoryPath();
            var logDirectory     = new LogDirectory(logDirectoryPath, new Settings(approximateMaximumFileLength: approxFileLength));

            using (var writer = logDirectory.GetWriter())
            {
                var data = Enumerable.Range(0, 1000)
                           .Select(n => $"THIS IS LINE NUMBER {n} OUT OF A LOT")
                           .Select(Encoding.UTF8.GetBytes);

                await writer.WriteManyAsync(data);
            }

            var directory = new DirectoryInfo(logDirectoryPath);

            directory.DumpDirectoryContentsToConsole();

            var dataFiles = directory.GetFiles("*.dat").OrderBy(f => f.FullName);

            foreach (var dataFile in dataFiles)
            {
                Assert.That(dataFile.Length, Is.LessThan(1.1 * approxFileLength));
            }
        }
Пример #5
0
        public async Task Writer()
        {
            var logDirectory = new LogDirectory(@"C:\data\kafkaesque");

            // hold on to this bad boy until your application shuts down
            using (var logWriter = logDirectory.GetWriter())
            {
                await logWriter.WriteAsync(new byte[] { 1, 2, 3 });
            }
        }
Пример #6
0
        Lazy <LogWriter> CreateWriter(string topic)
        {
            var topicDirectoryPath = Path.Combine(_directoryPath, topic);

            return(new Lazy <LogWriter>(() =>
            {
                var logDirectory = new LogDirectory(topicDirectoryPath, new Settings(logger: new KafkaesqueToToposLogger(_logger)));

                _logger.Debug("Initializing new Kafkaesque writer with path {directoryPath}", topicDirectoryPath);

                return logDirectory.GetWriter();
            }));
        }
Пример #7
0
        public async Task CheckBehaviorWhenWriterIsSlow()
        {
            var logDirectoryPath = GetLogDirectoryPath();
            var logDirectory     = new LogDirectory(logDirectoryPath, new Settings(logger: new SerilogLogger()));

            var writer = Using(logDirectory.GetWriter());

            var readEvents = new ConcurrentQueue <string>();

            ThreadPool.QueueUserWorkItem(_ =>
            {
                var cancellationToken = CancelOnDisposal();

                try
                {
                    var reader = logDirectory.GetReader();

                    foreach (var evt in reader.Read(cancellationToken: cancellationToken, throwWhenCancelled: true))
                    {
                        var text = Encoding.UTF8.GetString(evt.Data);
                        Console.WriteLine($"Reader loop read text: {text}");
                        readEvents.Enqueue(text);
                    }
                }
                catch (OperationCanceledException) when(cancellationToken.IsCancellationRequested)
                {
                    Console.WriteLine("Reader loop exited");
                }
            });

            async Task Write(string text)
            {
                Console.WriteLine($"Writing text: {text}");
                await writer.WriteAsync(Encoding.UTF8.GetBytes(text));
            }

            await Task.Run(async() =>
            {
                await Write("HEJ");
                await Task.Delay(TimeSpan.FromSeconds(1));
                await Write("MED");
                await Task.Delay(TimeSpan.FromSeconds(1));
                await Write("DIG");
                await Task.Delay(TimeSpan.FromSeconds(1));
                await Write("MIN");
                await Task.Delay(TimeSpan.FromSeconds(1));
                await Write("VÆÆÆÆN");
            });

            await readEvents.WaitFor(q => q.Count == 5, invariantExpression : q => q.Count >= 0 && q.Count <= 5, timeoutSeconds : 50);
        }
Пример #8
0
        public async Task CheckResumption_Eof(int count)
        {
            var directoryPath = GetLogDirectoryPath();
            var logDirectory  = new LogDirectory(directoryPath, new Settings(approximateMaximumFileLength: 4096, numberOfFilesToKeep: int.MaxValue));

            using (var writer = logDirectory.GetWriter())
            {
                var data = Enumerable.Range(0, count)
                           .Select(n => $"THIS IS LINE NUMBER {n}")
                           .Select(Encoding.UTF8.GetBytes);

                await writer.WriteManyAsync(data);
            }

            new DirectoryInfo(directoryPath).DumpDirectoryContentsToConsole();

            // read events in a very inefficient way, checking that we can resume at each single line
            var fileNumber   = -1;
            var bytePosition = -1;

            var linesRead = 0;

            for (var counter = 0; counter < count; counter++)
            {
                var expectedText = $"THIS IS LINE NUMBER {counter}";
                var data         = logDirectory.GetReader().ReadEof(fileNumber, bytePosition).FirstOrDefault();

                if (data == LogReader.EOF)
                {
                    break;
                }

                if (!(data is LogEvent eventData))
                {
                    continue;
                }

                var actualText = Encoding.UTF8.GetString(eventData.Data);

                Assert.That(actualText, Is.EqualTo(expectedText));

                fileNumber   = eventData.FileNumber;
                bytePosition = eventData.BytePosition;

                linesRead++;
            }

            Assert.That(linesRead, Is.EqualTo(count));
        }
Пример #9
0
        public async Task CanReadSomeEvents()
        {
            var logDirectoryPath = GetLogDirectoryPath();

            var log    = new LogDirectory(logDirectoryPath);
            var reader = log.GetReader();

            using var writer = log.GetWriter();

            await writer.WriteAsync(new byte[] { 1, 2, 3 });

            await writer.WriteAsync(new byte[] { 1, 2, 3 });

            await writer.WriteAsync(new byte[] { 1, 2, 3 });

            var list = reader.ReadEof().TakeWhile(e => e != LogReader.EOF).ToList();

            Assert.That(list.Count, Is.EqualTo(3));
        }
Пример #10
0
        public async Task WhatHappensIfWeWriteALot(int iterations, bool parallel)
        {
            SetLogLevel(LogEventLevel.Information);

            var logDirectoryPath = GetLogDirectoryPath();
            var logDirectory     = new LogDirectory(logDirectoryPath);

            var logWriter = logDirectory.GetWriter();

            Using(logWriter);

            var bytes = Enumerable.Range(0, 20000)
                        .Select(o => (byte)(iterations % 256))
                        .ToArray();

            var stopwatch = Stopwatch.StartNew();

            if (parallel)
            {
                await Task.WhenAll(Enumerable.Range(0, iterations)
                                   .Select(i => logWriter.WriteAsync(bytes)));
            }
            else
            {
                for (var counter = 0; counter < iterations; counter++)
                {
                    await logWriter.WriteAsync(bytes);
                }
            }

            var directoryInfo = new DirectoryInfo(logDirectoryPath);

            directoryInfo.DumpDirectoryContentsToConsole();

            var files = directoryInfo.GetFiles();

            var elapsedSeconds    = stopwatch.Elapsed.TotalSeconds;
            var totalBytesWritten = files.Sum(a => a.Length);

            Console.WriteLine($"Wrote {totalBytesWritten.FormatAsHumanReadableSize()} in {elapsedSeconds:0.0} s - that's {((long)(totalBytesWritten / elapsedSeconds)).FormatAsHumanReadableSize()}/s");
        }
Пример #11
0
        public async Task CanWriteAndReadItBack()
        {
            var logDirectoryPath = GetLogDirectoryPath();
            var logDirectory     = new LogDirectory(logDirectoryPath);

            var logWriter = logDirectory.GetWriter();

            Using(logWriter);

            await logWriter.WriteAsync(new byte[] { 1, 2, 3 }, CancelAfter(TimeSpan.FromSeconds(3)));

            var reader = logDirectory.GetReader();

            var logEvents = reader.Read(cancellationToken: CancelAfter(TimeSpan.FromSeconds(3))).ToList();

            Assert.That(logEvents.Count, Is.EqualTo(1));

            var logEvent = logEvents.First();

            Assert.That(logEvent.Data, Is.EqualTo(new byte[] { 1, 2, 3 }));
        }
Пример #12
0
        public async Task CanDeleteOldFiles()
        {
            var directoryInfo = new DirectoryInfo(GetLogDirectoryPath());
            var settings      = new Settings(numberOfFilesToKeep: 10, approximateMaximumFileLength: 32768);
            var logDirectory  = new LogDirectory(directoryInfo, settings);

            using (var writer = logDirectory.GetWriter())
            {
                var data = Enumerable.Range(0, 10000)
                           .Select(n => $"THIS IS LINE NUMBER {n} OUT OF QUITE A FEW")
                           .Select(Encoding.UTF8.GetBytes);

                await writer.WriteManyAsync(data);
            }

            directoryInfo.DumpDirectoryContentsToConsole();

            var dataFiles = directoryInfo.GetFiles("*.dat").ToList();

            Assert.That(dataFiles.Count, Is.EqualTo(10));
        }
Пример #13
0
        public async Task CanReadAndResumeAfterExperiencingEof()
        {
            var logDirectoryPath = GetLogDirectoryPath();

            var log    = new LogDirectory(logDirectoryPath);
            var reader = log.GetReader();

            using var writer = log.GetWriter();

            await writer.WriteAsync(new byte[] { 1, 2, 3 });

            await writer.WriteAsync(new byte[] { 1, 2, 3 });

            await writer.WriteAsync(new byte[] { 1, 2, 3 });

            var firstList = reader.ReadEof().TakeWhile(e => e != LogReader.EOF).Cast <LogEvent>().ToList();

            await writer.WriteAsync(new byte[] { 1, 2, 3 });

            await writer.WriteAsync(new byte[] { 1, 2, 3 });

            await writer.WriteAsync(new byte[] { 1, 2, 3 });

            await writer.WriteAsync(new byte[] { 1, 2, 3 });

            await writer.WriteAsync(new byte[] { 1, 2, 3 });

            var(fileNumber, bytePosition) = (firstList.Last().FileNumber, firstList.Last().BytePosition);

            var secondList = reader.ReadEof(fileNumber, bytePosition)
                             .TakeWhile(e => e != LogReader.EOF)
                             .ToList();

            Assert.That(firstList.Count, Is.EqualTo(3));
            Assert.That(secondList.Count, Is.EqualTo(5));
        }
Пример #14
0
        static async Task Main()
        {
            Log.Logger = new LoggerConfiguration()
                         .WriteTo.ColoredConsole()
                         .MinimumLevel.Verbose()
                         .CreateLogger();

            var count        = 100;
            var logDirectory = new LogDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "data"));
            var stopwatch    = Stopwatch.StartNew();

            using (var writer = logDirectory.GetWriter())
            {
                var messages = Enumerable.Range(0, count).Select(n => $"THIS IS MESSAGE NUMBER {n}");

                await writer.WriteManyAsync(messages.Select(Encoding.UTF8.GetBytes));

                //await Task.Delay(TimeSpan.FromSeconds(.1));
            }

            var elapsedSeconds = stopwatch.Elapsed.TotalSeconds;

            Console.WriteLine($"Wrote {count} messages in {elapsedSeconds:0.0} s - that's {count/elapsedSeconds:0.0} msg/s");
        }
Пример #15
0
        async Task RunTest(int count, int readerCount)
        {
            SetLogLevel(LogEventLevel.Information);

            var logDirectoryPath = GetLogDirectoryPath();
            var logDirectory     = new LogDirectory(logDirectoryPath);

            var messagesToWrite = Enumerable.Range(0, count)
                                  .Select(n => $"THIS IS A STRING MESSAGE/{n}")
                                  .ToConcurrentQueue();

            var cancellationTokenSource = new CancellationTokenSource();

            Using(cancellationTokenSource);

            var cancellationToken = cancellationTokenSource.Token;

            var writer = logDirectory.GetWriter();

            Using(writer);

            var readerThreads = Enumerable
                                .Range(0, readerCount)
                                .Select(n =>
            {
                var logReader    = logDirectory.GetReader();
                var readMessages = new ConcurrentQueue <string>();

                return(new
                {
                    Thread = new Thread(() =>
                    {
                        try
                        {
                            foreach (var logEvent in logReader.Read(cancellationToken: cancellationToken))
                            {
                                readMessages.Enqueue(Encoding.UTF8.GetString(logEvent.Data));
                            }
                        }
                        catch (OperationCanceledException) when(cancellationToken.IsCancellationRequested)
                        {
                            Console.WriteLine($"Thread {n} was cancelled");
                        }
                        catch (Exception exception)
                        {
                            Console.WriteLine(exception);
                        }
                    })
                    {
                        IsBackground = true,
                        Name = $"Reader thread {n}"
                    },

                    Messages = readMessages
                });
            })
                                .ToList();

            readerThreads.ForEach(a => a.Thread.Start());

            var writerThreads = Enumerable.Range(0, 10)
                                .Select(n => new Thread(() =>
            {
                while (messagesToWrite.TryDequeue(out var message))
                {
                    writer.WriteAsync(Encoding.UTF8.GetBytes(message), cancellationToken);
                }
            })
            {
                Name = $"Writer thread {n}"
            })
Пример #16
0
        public async Task CanReadBackEventsSpreadOverMultipleFiles_WritingEverythingInAdvance(int count)
        {
            SetLogLevel(LogEventLevel.Verbose);

            var messages = Enumerable.Range(0, count)
                           .Select(n => $"{n}/This is a pretty long string message, whose purpose is solely to take up a lot of space, meaning that the events will eventually need to be placed in more than one file.");

            var logDirectoryPath = GetLogDirectoryPath();
            var directoryInfo    = new DirectoryInfo(logDirectoryPath);
            var logDirectory     = new LogDirectory(directoryInfo);

            var writeStopwatch = Stopwatch.StartNew();

            // write everything
            var writer = logDirectory.GetWriter();

            Using(writer);
            await writer.WriteManyAsync(messages.Select(Encoding.UTF8.GetBytes));

            var elapsedSecondsWriting = writeStopwatch.Elapsed.TotalSeconds;

            Console.WriteLine($"Wrote {count} messages in {elapsedSecondsWriting:0.0} s - that's {count/elapsedSecondsWriting:0.0} msg/s");

            directoryInfo.DumpDirectoryContentsToConsole();

            // read it back
            var reader                = logDirectory.GetReader();
            var readStopwatch         = Stopwatch.StartNew();
            var expectedMessageNumber = 0;

            foreach (var message in reader.Read(cancellationToken: CancelAfter(TimeSpan.FromSeconds(20))).Take(count))
            {
                var text  = Encoding.UTF8.GetString(message.Data);
                var parts = text.Split('/');

                try
                {
                    if (parts.Length != 2)
                    {
                        throw new FormatException(
                                  $"The text '{text}' could not be parsed - expected a number and a slash, followed by some text");
                    }

                    if (!int.TryParse(parts.First(), out var actualMessageNumber))
                    {
                        throw new FormatException(
                                  $"Could not parse the token '{parts.First()}' from the message '{text}' into an integer");
                    }

                    if (actualMessageNumber != expectedMessageNumber)
                    {
                        throw new AssertionException(
                                  $"The message number {actualMessageNumber} did not match the expected: {expectedMessageNumber}");
                    }
                }
                catch (Exception exception)
                {
                    throw new ApplicationException($"Error processing event with fileNumber = {message.FileNumber}, bytePosition = {message.BytePosition}", exception);
                }

                expectedMessageNumber++;
            }

            var elapsedSeconds = readStopwatch.Elapsed.TotalSeconds;

            Console.WriteLine($"Read {count} messages in {elapsedSeconds:0.0} s - that's {count/elapsedSeconds:0.0} msg/s");

            Assert.That(expectedMessageNumber, Is.EqualTo(count));
        }
Пример #17
0
        public async Task CanReadBackEventsSpreadOverMultipleFiles_ReadingWhileWriting(int count)
        {
            SetLogLevel(LogEventLevel.Verbose);

            var messages = Enumerable.Range(0, count)
                           .Select(n => $"{n}/This is a pretty long string message, whose purpose is solely to take up a lot of space, meaning that the events will eventually need to be placed in more than one file.");

            var logDirectoryPath = GetLogDirectoryPath();
            var directoryInfo    = new DirectoryInfo(logDirectoryPath);
            var logDirectory     = new LogDirectory(directoryInfo);

            var doneWriting = Using(new ManualResetEvent(false));

            var writer = logDirectory.GetWriter();

            // ensure that the background thread has finished writing before we dispose the writer
            Using(new DisposableCallback(() =>
            {
                using (writer)
                {
                    var timeout = TimeSpan.FromMinutes(1);
                    if (!doneWriting.WaitOne(timeout))
                    {
                        Console.WriteLine($"WARNING: WRITE OPERATION WAS NOT COMPLETED WITHIN {timeout} TIMEOUT");
                    }
                }
            }));

            ThreadPool.QueueUserWorkItem(async _ =>
            {
                try
                {
                    await writer.WriteManyAsync(messages.Select(Encoding.UTF8.GetBytes));

                    directoryInfo.DumpDirectoryContentsToConsole();
                }
                finally
                {
                    doneWriting.Set();
                }
            });

            var reader = logDirectory.GetReader();
            var expectedMessageNumber = 0;
            var stopwatch             = Stopwatch.StartNew();

            foreach (var message in reader.Read(cancellationToken: CancelAfter(TimeSpan.FromSeconds(20))).Take(count))
            {
                var text  = Encoding.UTF8.GetString(message.Data);
                var parts = text.Split('/');

                try
                {
                    if (parts.Length != 2)
                    {
                        throw new FormatException(
                                  $"The text '{text}' could not be parsed - expected a number and a slash, followed by some text");
                    }

                    if (!int.TryParse(parts.First(), out var actualMessageNumber))
                    {
                        throw new FormatException(
                                  $"Could not parse the token '{parts.First()}' from the message '{text}' into an integer");
                    }

                    if (actualMessageNumber != expectedMessageNumber)
                    {
                        throw new AssertionException(
                                  $"The message number {actualMessageNumber} did not match the expected: {expectedMessageNumber}");
                    }
                }
                catch (Exception exception)
                {
                    throw new ApplicationException($"Error processing event with fileNumber = {message.FileNumber}, bytePosition = {message.BytePosition}", exception);
                }

                expectedMessageNumber++;
            }

            var elapsedSeconds = stopwatch.Elapsed.TotalSeconds;

            Console.WriteLine($"Read {count} messages in {elapsedSeconds:0.0} s - that's {count/elapsedSeconds:0.0} msg/s");

            Assert.That(expectedMessageNumber, Is.EqualTo(count));
        }