Rò rỉ bộ nhớ InfluxDB trong ứng dụng Windows Service .NET

Môi trường triển khai

  • Loại dự án: Windows Service
  • Phiên bản .NET: .NET Framework 4.8
  • Ngôn ngữ lập trình: C#
  • Thư viện InfluxDB: InfluxDB.Client v4.15.0, InfluxDB.Client.Core v4.15.0

Mã tái tạo sự cố

Thiết lập Timer để gọi hàm ghi dữ liệu theo chu kỳ:

_periodicTimer = new Timer((o) => SaveMetricData(), null, intervalMinutes * 1000, 60 * 1000); // mỗi 1 phút

Cài đặt hàm SaveMetricData:

private async void SaveMetricData()
{
    try
    {
        lock (_dataLock)
        {
            string currentTime = DateTime.Now.ToString("HH:mm:ss");
            foreach (DataRow row in productionSchedule.Rows)
            {
                if (string.Compare(currentTime, row["startTime"].ToString()) >= 0
                    && string.Compare(currentTime, row["endTime"].ToString()) < 0)
                {
                    var metricPoint = PointData.Measurement("production_metrics")
                    .Tag("factoryId", row["factoryId"].ToString())
                    .Tag("workshopId", row["workshopId"].ToString())
                    .Tag("lineId", row["lineId"].ToString())
                    .Tag("teamId", row["teamId"].ToString())
                    .Tag("shiftType", row["shiftType"].ToString())
                    .Field("scheduledDuration", 60); // ghi mỗi phút, tức 60s mỗi lần

                    var writeApi = _influxDBClient.GetWriteApi();
                    writeApi.WritePoint(metricPoint, bucket, org);
                    _logger.Info($"Đã ghi dữ liệu cho team: {row["teamId"]}");
                }
            }
        }
    }
    catch (Exception ex)
    {
        _logger.Error(ex.ToString());
    }
}

Giải pháp khắc phục

Chuyển đổi từ phương thức đồng bộ GetWriteApi sang phương thức bất đồng bộ GetWriteApiAsync:

private void SaveMetricData()
{
    try
    {
        lock (_dataLock)
        {
            string currentTime = DateTime.Now.ToString("HH:mm:ss");
            List<PointData> metricPoints = new List<PointData>();
            
            foreach (DataRow row in productionSchedule.Rows)
            {
                if (string.Compare(currentTime, row["startTime"].ToString()) >= 0
                    && string.Compare(currentTime, row["endTime"].ToString()) < 0)
                {
                    var metricPoint = PointData.Measurement("production_metrics")
                    .Tag("factoryId", row["factoryId"].ToString())
                    .Tag("workshopId", row["workshopId"].ToString())
                    .Tag("lineId", row["lineId"].ToString())
                    .Tag("teamId", row["teamId"].ToString())
                    .Tag("shiftType", row["shiftType"].ToString())
                    .Field("scheduledDuration", 60); // ghi mỗi phút, tức 60s mỗi lần
                    
                    metricPoints.Add(metricPoint);
                }
            }
            
            var writeApi = _influxDBClient.GetWriteApiAsync();
            writeApi.WritePointsAsync(metricPoints, bucket, org);
        }
    }
    catch (Exception ex)
    {
        _logger.Error(ex.ToString());
    }
}

Kết luận

Đây là một lỗi tiềm ẩn trong thư viện InfluxDB.Client. Việc sử dụng phương thức GetWriteApi() đồng bộ trong môi trường đa luồng với Timer gây ra tình trạng rò rỉ bộ nhớ theo thời gian. Giải pháp thay thế bằng GetWriteApiAsync() kết hợp với việc thu thập các điểm dữ liệu vào danh sách trước khi ghi sẽ khắc phục được vấn đề này.

Thẻ: InfluxDB .NET C# memory-leak Windows-service

Đăng vào ngày 29 tháng 6 lúc 10:51