예제 #1
0
 public CompressedPage(MessagePageId pageId, IReadOnlyList <MessageContentGrpcModel> messages)
 {
     ZippedContent = messages.ZipMessages();
     Messages      = messages;
     PageId        = pageId;
     CalcMinMax();
 }
예제 #2
0
 public CompressedPage(MessagePageId pageId, ReadOnlyMemory <byte> zippedContent)
 {
     ZippedContent = zippedContent;
     Messages      = zippedContent.UnzipMessages();
     PageId        = pageId;
     CalcMinMax();
 }
예제 #3
0
        private async Task TestPageWriteAndRead(PagesCluster clusterPage, long pageId, Dictionary <long, byte[]> cache)
        {
            var messagePageId = new MessagePageId(pageId);
            var hasPage       = await clusterPage.HasPageAsync(messagePageId);

            Assert.IsFalse(hasPage);


            var msg = new MessageContentGrpcModel
            {
                Created   = DateTime.UtcNow,
                Data      = new [] { (byte)pageId, (byte)pageId, (byte)pageId },
                MessageId = pageId,
            };

            var content = new CompressedPage(new MessagePageId(pageId), new [] { msg });

            cache.Add(pageId, content.ZippedContent.ToArray());

            await clusterPage.WriteAsync(messagePageId, content);

            hasPage = await clusterPage.HasPageAsync(messagePageId);

            Assert.IsTrue(hasPage);

            var resultContent = await clusterPage.ReadAsync(messagePageId);

            resultContent.ZippedContent.AssertAllBytesAreEqualWith(content.ZippedContent);
        }
예제 #4
0
        private async Task DoubleCheck(PagesCluster clusterPage, long pageId, byte[] srcContent)
        {
            var messagePageId = new MessagePageId(pageId);
            var resultContent = await clusterPage.ReadAsync(messagePageId);

            Assert.AreEqual(resultContent.Messages[0].MessageId, pageId);
            resultContent.ZippedContent.AssertAllBytesAreEqualWith(srcContent);
        }
        public async ValueTask <bool> HasPageAsync(MessagePageId pageId)
        {
            var tocIndex = GetPageTocIndex(pageId);

            var(_, length) = await GetPagePositionAllocationToc(tocIndex, false);

            return(length > 0);
        }
예제 #6
0
        public void Update(MessagePageId pageId, long blobPosition, long maxSavedMessageId)
        {
            if (MaxSavedMessageId < maxSavedMessageId)
            {
                MaxSavedMessageId = maxSavedMessageId;
            }

            if (PageId.Value <= pageId.Value)
            {
                BlobPosition = blobPosition;
                PageId       = pageId;
            }
        }
예제 #7
0
        public async Task <CompressPageResult> CompressAsync([FromForm] string topicId, [FromForm] long pageId)
        {
            var snapshot = ServiceLocator.QueueSnapshotCache.Get();

            var topicSnapshot = snapshot.Cache.FirstOrDefault(itm => itm.TopicId == topicId);

            if (topicSnapshot == null)
            {
                return new CompressPageResult
                       {
                           Result = "Topic not found"
                       }
            }
            ;


            var messagePageId = new MessagePageId(pageId);

            if (!ServiceLocator.CompressedMessagesUtils.PageCanBeCompressed(topicId, messagePageId))
            {
                return(new CompressPageResult
                {
                    Result = "Can not compress current page"
                });
            }

            await ServiceLocator.TaskSchedulerByTopic.ExecuteTaskAsync(topicId, messagePageId,
                                                                       "Compressing page by REST API request",
                                                                       async() =>
            {
                var page = ServiceLocator.MessagesContentCache.TryGetPage(topicId, messagePageId);

                if (page == null)
                {
                    await ServiceLocator.MessagesContentReader.TryGetPageTopicThreadSynchronizedAsync(topicId,
                                                                                                      messagePageId);
                }

                if (page != null)
                {
                    await ServiceLocator.CompressPageBlobOperation.ExecuteOperationThreadTopicSynchronizedAsync(
                        topicId, messagePageId, page);
                }
            });

            return(new CompressPageResult
            {
                Result = "Page is compressed"
            });
        }
예제 #8
0
        public bool PageCanBeCompressed(string topicId, MessagePageId pageId)
        {
            var snapshot = _queueSnapshotCache.Get();

            var topicSnapshot = snapshot.Cache.FirstOrDefault(itm => itm.TopicId == topicId);

            if (topicSnapshot == null)
            {
                return(false);
            }

            var activePageId = MessagesContentPagesUtils.GetPageId(topicSnapshot.MessageId);

            return(pageId.Value < activePageId.Value - 1);
        }
예제 #9
0
        public ValueTask SaveLastCompressedPageStorageAsync(string topicId, MessagePageId pageId)
        {
            lock (_lastPages)
            {
                if (_lastPages.ContainsKey(topicId))
                {
                    _lastPages[topicId] = pageId.Value;
                }
                else
                {
                    _lastPages.Add(topicId, pageId.Value);
                }
                _hasDataToUpdate = true;
            }

            return(new ValueTask());
        }
        public async Task WriteAsync(MessagePageId pageId, CompressedPage pageData)
        {
            var tocIndex = GetPageTocIndex(pageId);

            if (_tocPage == null)
            {
                await InitIndexPageAsync(true);
            }

            var nextPageNoToWrite = await GetNextPageNoToWriteAsync(tocIndex, pageData.ZippedContent.Length);

            await AzurePageBlob.WriteBytesAsync(pageData.ZippedContent, nextPageNoToWrite, new WriteBytesOptions
            {
                SplitRoundTripsPageSize = 4096
            });

            await WritePagePositionAllocationToc(tocIndex, nextPageNoToWrite, pageData.ZippedContent.Length);
        }
예제 #11
0
        public async ValueTask <IActionResult> Download([Required][FromQuery] string topicId, [Required][FromQuery] long pageId)
        {
            var messagePageId = new MessagePageId(pageId);
            var page          = ServiceLocator.MessagesContentCache.TryGetPage(topicId, messagePageId);

            if (page == null)
            {
                await ServiceLocator.TaskSchedulerByTopic.ExecuteTaskAsync(topicId, messagePageId, "Load Debug Page",
                                                                           async() =>
                {
                    await ServiceLocator.MessagesContentReader.LoadPageIntoCacheTopicSynchronizedAsync(topicId,
                                                                                                       messagePageId);
                });
            }

            page = ServiceLocator.MessagesContentCache.TryGetPage(topicId, messagePageId);
            return(File(page.GetCompressedPage().ZippedContent.ToArray(), pageId + "application/zip", topicId + '-' + pageId + ".zip"));
        }
        public async Task <CompressedPage> ReadAsync(MessagePageId pageId)
        {
            try
            {
                //If we do not have exceptions - can remove this try/catch
                GetPageTocIndex(pageId);
            }
            catch (Exception e)
            {
                Console.WriteLine("Can not get TocIndex for page: " + pageId.Value + "; TopicId:" + TopicId);
                Console.WriteLine(e);
                throw;
            }

            var tocIndex = GetPageTocIndex(pageId);

            var(position, length) = await GetPagePositionAllocationToc(tocIndex, false);

            if (length == 0)
            {
                return(CompressedPage.CreateEmpty(pageId));
            }

            try
            {
                //If we do not have exceptions - can remove this try/catch

                var fullPages = MyAzurePageBlobUtils.CalculateRequiredPagesAmount(length);

                var result = await AzurePageBlob.ReadAsync(position, fullPages);

                var buffer = result.GetBuffer();

                return(new CompressedPage(pageId, new ReadOnlyMemory <byte>(buffer, 0, (int)length)));
            }
            catch (Exception)
            {
                Console.WriteLine("Problem with reading page: " + pageId + " TocIndex: " + tocIndex + "; position:" + position + " Length:" + length);
                return(CompressedPage.CreateEmpty(pageId));
            }
        }
예제 #13
0
        private static void PopulatePages(this List <MessagePageId> pages, MessagePageId pageId, MessagePageId maxPageId)
        {
            if (pageId.Value <= maxPageId.Value)
            {
                if (pages.All(itm => itm.Value != pageId.Value))
                {
                    pages.Add(pageId);
                }
            }


            var nextPageId = pageId.Value + 1;

            if (nextPageId <= maxPageId.Value)
            {
                if (pages.All(itm => itm.Value != nextPageId))
                {
                    pages.Add(new MessagePageId(nextPageId));
                }
            }
        }
예제 #14
0
        private async Task GcThreadTopicSynchronizedAsync(string topicId, MessagePageId pageId)
        {
            var page = _messagesContentCache.TryGetPage(topicId, pageId);

            if (page == null)
            {
                return;
            }

            var gcResult = await _messagesContentPersistentStorage.TryToGcAsync(topicId, pageId);

            if (gcResult.NotFound)
            {
                _logger.AddLog(LogProcess.PagesLoaderOrGc, topicId, "PageNo:" + pageId, "Attempt to GC PageWriter which is not found. Disposing page from the Cache");
                _messagesContentCache.DisposePage(topicId, pageId);
                return;
            }


            if (gcResult.NotReadyToGc)
            {
                _logger.AddLog(LogProcess.PagesLoaderOrGc, topicId, "PageNo:" + pageId, "Attempt to GC PageWriter which has not synced messages. Trying to Sync them to the Blob and skipping for the next round");
                await _messagesContentPersistentStorage.SyncAsync(topicId, pageId);

                return;
            }



            if (gcResult.DisposedPageWriter != null)
            {
                _logger.AddLog(LogProcess.PagesLoaderOrGc, topicId, "PageNo:" + pageId, "PageWriter disposed Ok.. Disposing From Cache");

                await _compressPageBlobOperation.ExecuteOperationThreadTopicSynchronizedAsync(topicId, pageId, gcResult.DisposedPageWriter.AssignedPage);

                _messagesContentCache.DisposePage(topicId, pageId);

                _logger.AddLog(LogProcess.PagesLoaderOrGc, topicId, "PageNo:" + pageId, "Page is disposed from Cache");
            }
        }
        public Dictionary <string, ActivePagesByTopic> GetActivePages()
        {
            var(_, cache) = _queueSnapshotCache.Get();

            var result = new Dictionary <string, ActivePagesByTopic>();

            foreach (var topicSnapshot in cache)
            {
                var pages = new List <MessagePageId>
                {
                    MessagePageId.CreateFromMessageId(topicSnapshot.MessageId)
                };
                foreach (var queueSnapshot in topicSnapshot.QueueSnapshots)
                {
                    foreach (var range in queueSnapshot.Ranges)
                    {
                        var fromPageId = MessagePageId.CreateFromMessageId(range.FromId);
                        var toPageId   = MessagePageId.CreateFromMessageId(range.ToId);

                        if (pages.All(itm => itm.Value != fromPageId.Value))
                        {
                            pages.Add(fromPageId);
                        }

                        if (pages.All(itm => itm.Value != toPageId.Value))
                        {
                            pages.Add(toPageId);
                        }
                    }
                }

                result.Add(topicSnapshot.TopicId, new ActivePagesByTopic
                {
                    Snapshot = topicSnapshot,
                    Pages    = pages
                });
            }

            return(result);
        }
예제 #16
0
        public async ValueTask <IActionResult> Index([Required][FromQuery] string topicId, [Required][FromQuery] long pageId)
        {
            var messagePageId = new MessagePageId(pageId);
            var page          = ServiceLocator.MessagesContentCache.TryGetPage(topicId, messagePageId);

            if (page == null)
            {
                await ServiceLocator.TaskSchedulerByTopic.ExecuteTaskAsync(topicId, messagePageId, "Load Debug Page",
                                                                           async() =>
                {
                    await ServiceLocator.MessagesContentReader.LoadPageIntoCacheTopicSynchronizedAsync(topicId,
                                                                                                       messagePageId);
                });
            }

            page = ServiceLocator.MessagesContentCache.TryGetPage(topicId, messagePageId);

            if (page == null)
            {
                return(NotFound("Page Not Found"));
            }


            var result = new
            {
                page.MinMessageId,
                page.MaxMessageId,
                page.Count,
                shouldHaveAmount = page.ShouldHaveAmount(),
                hasSkipped       = page.HasSkipped(),
                notSavedAmount   = page.NotSavedAmount,
                hashCode         = page.GetHashCode()
            };


            return(Json(result));
        }
예제 #17
0
 public static CompressedPage CreateEmpty(MessagePageId pageId)
 {
     return(new (pageId, ReadOnlyMemory <byte> .Empty));
 }
        private int GetPageTocIndex(MessagePageId pageId)
        {
            var firstPageNoOnCompressedPage = ClusterPageId.GetFirstPageIdOnCompressedPage();

            return((int)(pageId.Value - firstPageNoOnCompressedPage.Value));
        }
예제 #19
0
        public async ValueTask SaveMessagesAsync(IAsyncEnumerable <CompressedMessageChunkModel> request)
        {
            if (!ServiceLocator.AppGlobalFlags.Initialized)
            {
                throw new Exception("App is not initialized yet");
            }

            if (ServiceLocator.AppGlobalFlags.IsShuttingDown)
            {
                throw new Exception("App is stopping");
            }


            var contract = await request.DecompressAndMerge <SaveMessagesGrpcContract>();

            if (contract.Messages == null)
            {
                Console.WriteLine(contract.TopicId + ": Request to Save messages with empty content");
                return;
            }
            ServiceLocator.IndexByMinuteWriter.NewMessages(contract.TopicId, contract.Messages);

            var groups = contract.Messages
                         .GroupBy(itm => MessagesContentPagesUtils.GetPageId(itm.MessageId).Value);

            foreach (var group in groups)
            {
                var messagePageId = new MessagePageId(group.Key);

                var writablePage = ServiceLocator.MessagesContentCache.TryGetWritablePage(contract.TopicId, messagePageId);

                if (writablePage != null)
                {
                    writablePage.NewMessages(group);
                }
                else
                {
                    var result =
                        ServiceLocator.MessagesContentCache.CreateWritablePage(contract.TopicId, messagePageId);

                    if (result.Result != null)
                    {
                        result.Result.NewMessages(group);
                        continue;
                    }

                    if (result.Exists != null)
                    {
                        if (result.Exists is WritableContentPage writableContentPage)
                        {
                            ServiceLocator.AppLogger.AddLog(LogProcess.NewMessages, contract.TopicId,
                                                            "PageId: " + group.Key, "Creating new writable content page which exists. Reusing it");
                            writableContentPage.NewMessages(group);
                            continue;
                        }

                        ServiceLocator.AppLogger.AddLog(LogProcess.NewMessages, contract.TopicId,
                                                        "PageId: " + group.Key,
                                                        "Trying to add messages, but readOnly messages content is found. Converting it into Writable");


                        ServiceLocator.MessagesContentCache
                        .ConvertIntoWritable(contract.TopicId, result.Exists).NewMessages(group);

                        continue;
                    }


                    ServiceLocator.AppLogger.AddLog(LogProcess.NewMessages, contract.TopicId,
                                                    "PageId: " + group.Key,
                                                    "Trying to add messages, but readOnly messages content is found. Should not be here. Skipping messages");
                }
            }
        }
예제 #20
0
        public async Task ExecuteOperationThreadTopicSynchronizedAsync(string topicId, MessagePageId pageId, IMessageContentPage messageContentPage)
        {
            var logContext = "Page: " + pageId.Value;

            if (await _compressedMessagesStorage.HasCompressedPageAsync(topicId, pageId))
            {
                _appLogger.AddLog(LogProcess.PagesCompressor, topicId, logContext, "Has compressed page. Skipping compressing procedure");
                return;
            }


            if (messageContentPage.Count == 0)
            {
                _appLogger.AddLog(LogProcess.PagesCompressor, topicId, logContext, "No messages to compress. Skipping compressing procedure");
                return;
            }

            var compressedPage = messageContentPage.GetCompressedPage();

            if (_appGlobalFlags.DebugTopic == topicId)
            {
                _appLogger.AddLog(LogProcess.PagesCompressor, topicId, logContext, $"Writing Compressed data for page {pageId}.");
            }

            await _compressedMessagesStorage.WriteCompressedPageAsync(topicId, pageId, compressedPage, _appLogger);

            if (_appGlobalFlags.DebugTopic == topicId)
            {
                _appLogger.AddLog(LogProcess.PagesCompressor, topicId, logContext, $"Verifying compressed data for page {pageId}");
            }

            var compressedPageToVerify = await _compressedMessagesStorage.GetCompressedPageAsync(topicId, pageId);

            var messages = compressedPageToVerify.Messages;

            if (_appGlobalFlags.DebugTopic == topicId)
            {
                _appLogger.AddLog(LogProcess.PagesCompressor, topicId, logContext, $"Verified compressed data for page {pageId}. Messages: " + messages.Count);
            }

            if (_appGlobalFlags.DebugTopic == topicId)
            {
                _appLogger.AddLog(LogProcess.PagesCompressor, topicId, logContext, $"Deleting Uncompressed page {pageId}");
            }

            await _persistentStorage.DeleteNonCompressedPageAsync(topicId, pageId);

            if (_appGlobalFlags.DebugTopic == topicId)
            {
                _appLogger.AddLog(LogProcess.PagesCompressor, topicId, logContext, "Written Compressed Page: " + pageId + ". Messages in the page:" + messageContentPage.Count);
            }
        }
예제 #21
0
 public static MessagePageId PrevPage(this MessagePageId pageId)
 {
     return(new MessagePageId(pageId.Value + 1));
 }
예제 #22
0
 public bool EqualsWith(MessagePageId messagePageId)
 {
     return(Value == messagePageId.Value);
 }
예제 #23
0
 private async Task WarmUpThreadTopicSynchronizedAsync(string topicId, MessagePageId pageId)
 {
     _logger.AddLog(LogProcess.PagesLoaderOrGc, topicId, "PageNo:" + pageId, "Warming up the page");
     await _messagesContentReader.LoadPageIntoCacheTopicSynchronizedAsync(topicId, pageId);
 }
 public ReadOnlyContentPage(CompressedPage compressedPage)
 {
     PageId          = compressedPage.PageId;
     _compressedPage = compressedPage;
 }
        public async Task <IMessageContentPage> TryRestoreFromCompressedPage(string topicId, MessagePageId pageId)
        {
            var resultFromCache = _messagesContentCache.TryGetPage(topicId, pageId);

            if (resultFromCache != null)
            {
                return(resultFromCache);
            }


            var logContext = "PageId: " + pageId.Value;

            _appLogger.AddLog(LogProcess.PagesLoaderOrGc, topicId, logContext,
                              $"Restoring page #{pageId} from compressed source");

            var pageCompressedContent = await _compressedMessagesStorage.GetCompressedPageAsync(topicId, pageId);

            var dt = DateTime.UtcNow;

            if (pageCompressedContent.ZippedContent.Length == 0)
            {
                _appLogger.AddLog(LogProcess.PagesLoaderOrGc, topicId, logContext,
                                  $"Can not restore page #{pageId} from compressed source. Duration: {DateTime.UtcNow - dt}");
                return(null);
            }

            var  msgs  = pageCompressedContent.Messages;
            long minId = 0;
            long maxId = 0;

            if (msgs.Count > 0)
            {
                minId = msgs.Min(itm => itm.MessageId);
                maxId = msgs.Max(itm => itm.MessageId);
            }

            _appLogger.AddLog(LogProcess.PagesLoaderOrGc, topicId, logContext,
                              $"Restored page #{pageId} from compressed source. Duration: {DateTime.UtcNow - dt}. Messages: {msgs.Count}. MinId: {minId}, MaxId: {maxId}");


            var result = new ReadOnlyContentPage(pageCompressedContent);

            _messagesContentCache.AddPage(topicId, result);
            return(result);
        }
예제 #26
0
        public static ClusterPageId GetClusterPageId(this MessagePageId messagePageId)
        {
            var result = messagePageId.Value / PagesOnCluster;

            return(new ClusterPageId(result));
        }
예제 #27
0
 public MessagesPage(MessagePageId pageId)
 {
     PageId = pageId;
 }
        public async Task <IMessageContentPage> TryRestoreFromUncompressedPage(string topicId, MessagePageId pageId)
        {
            var logContext = "PageId: " + pageId.Value;

            var resultFromCache = _messagesContentCache.TryGetPage(topicId, pageId);

            if (resultFromCache != null)
            {
                return(resultFromCache);
            }


            _appLogger.AddLog(LogProcess.PagesLoaderOrGc, topicId, logContext,
                              $"Restoring page #{pageId} from UnCompressed source");

            var dt = DateTime.UtcNow;

            var pageWriter = await _messagesContentPersistentStorage.TryGetAsync(topicId, pageId,
                                                                                 () =>
            {
                var result = _messagesContentCache.CreateWritablePage(topicId, pageId);

                if (result.Result != null)
                {
                    return(result.Result);
                }


                if (result.Exists != null)
                {
                    if (result.Exists is WritableContentPage existingWritablePage)
                    {
                        _appLogger.AddLog(LogProcess.PagesLoaderOrGc, topicId, logContext, "Restoring uncompressed page and found existing writable content page. Using it");
                        return(existingWritablePage);
                    }

                    throw new Exception(
                        $"Trying to create page {topicId}/{pageId} by found non writable content page {result.Exists.GetType()}");
                }

                throw new Exception($"RestorePageFromBlobOperation.TryRestoreFromUncompressedPage  I should not be here. {topicId}/{pageId}");
            });

            if (pageWriter == null)
            {
                _appLogger.AddLog(LogProcess.PagesLoaderOrGc, topicId, logContext,
                                  "Can not restore page from uncompressed page");
                return(null);
            }


            _appLogger.AddLog(LogProcess.PagesLoaderOrGc, topicId, logContext,
                              $"Restored page #{pageId} from UnCompressed source. Duration: {DateTime.UtcNow - dt}. Messages: " +
                              pageWriter.AssignedPage.Count);

            return(pageWriter.AssignedPage);
        }