Azure Клиентская библиотека хранилища BLOB-объектов для. NET, вызывающая утечку памяти на Linux - PullRequest
0 голосов
/ 05 апреля 2020

У нас есть служба, которая генерирует сообщения и записывает их в добавочный BLOB-объект, это прекрасно работает на машине windows, но тот же код, опубликованный и работающий на машине linux, вызовет утечку памяти в процессе, даже если фактическое потребление памяти очень мало (при печати G C .GetTotalMemory). Я создал небольшое консольное приложение, которое имитирует то, что должен делать наш сервис, а также отображает утечку памяти (хотя и более медленную). (код приведен ниже). Наша главная цель состояла в том, чтобы этот сервис работал в Кубернетесе, где он легко перевалил за 750 МБ памяти за часы.

В нашей тестовой среде используется следующая версия: Azure .Storage.Blobs 12.4.0 Ubuntu 16.04,. net core 3.1

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Specialized;

namespace StorageTester
{
    class Program
    {
        static BlobContainerClient _container;
        static async Task Main(string[] args)
        {
            List<Reading> fakeBatch = new List<Reading>(20000);
            var connectionString = < yourConnectionString >
            _container = new BlobContainerClient(connectionString, "testblob");
            Random r = new Random();

            while (true)
            {
                string deviceId = r.Next(0, 1000).ToString();

                Reading rawReading = new Reading()
                {
                    AppId = "MyAppId",
                    DeviceId = deviceId,
                    ProductId = "MyProdId",
                    ReadingTimestamp = DateTime.Now,
                    Name = "Temp",
                    TypeCode = 70,
                    RawData =
                        "{\"app_id\":\"MyAppId\",\"dateTime\":\"2020-03-09T08:00:00.1210903Z\",\"type\":70,\"name\":\"Temp\",\"product_id\":\"MyProdId\",\"device_id\":\"MyDeviceId\",\"value\":\"123456\"}"
                };

                fakeBatch.Add(rawReading);
                if (fakeBatch.Count == 10000)
                {
                    await UploadBulk(fakeBatch);
                    fakeBatch.Clear();
                    Console.WriteLine("Memory used before collection: {0:N0}",
                        GC.GetTotalMemory(false));

                    // Collect all generations of memory.
                    GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized, false);
                    Console.WriteLine("Memory used after optimized background collection:   {0:N0}",
                        GC.GetTotalMemory(true));
                    Thread.Sleep(10000);

                }

            }
        }

        private static string CreateAllValuesKey(string appId, string deviceId, int typeCode, string eventName, DateTime date)
        {
            return appId + "/" + deviceId + "/" + typeCode + "/" + eventName + "/" + date.ToString("yy-MM-dd");
        }
        private static async Task Upload(KeyValuePair<string, string> keyValuePair)
        {

            AppendBlobClient appendBlob = _container.GetAppendBlobClient(keyValuePair.Key);

            if (!(await appendBlob.ExistsAsync()))
            {
                await appendBlob.CreateAsync();
                await AppendText(appendBlob, keyValuePair.Value);
            }
            else
            {
                await AppendText(appendBlob, "," + keyValuePair.Value);
            }
        }

        private static async Task AppendText(AppendBlobClient appendBlob, string textToAppend)
        {
            // use append block as append text seems to have an error at the moment.
            using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(textToAppend)))
            {
                await appendBlob.AppendBlockAsync(ms);
            }
        }

        private static async Task UploadBulk(List<Reading> input)
        {

            var groupedReadings = input.GroupBy(g => new
            { g.AppId, g.DeviceId, g.TypeCode, g.Name, g.ReadingTimestamp.ToUniversalTime().Date });

            List<Task> tasks = new List<Task>();
            foreach (var readingGroup in groupedReadings)
            {
                if (readingGroup.Any())
                {
                    var readingSample = readingGroup.First();

                    string appId = readingSample.AppId;
                    var key = CreateAllValuesKey(appId, readingSample.DeviceId, readingSample.TypeCode,
                        readingSample.Name, readingSample.ReadingTimestamp.ToUniversalTime().Date);

                    var resultString = string.Join(",", readingGroup.Select(r => r.RawData));
                    tasks.Add(Upload(new KeyValuePair<string, string>(key, resultString)));
                }
            }


            await Task.WhenAll(tasks);
        }

        internal class Reading
        {
            public string AppId { get; set; }
            public string DeviceId { get; set; }
            public string ProductId { get; set; }
            public DateTime ReadingTimestamp { get; set; }
            public string Name { get; set; }
            public int TypeCode { get; set; }
            public string RawData { get; set; }
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...