Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory leak when ES is not accesible #82

Open
lukas-lansky opened this issue Nov 7, 2016 · 1 comment
Open

Memory leak when ES is not accesible #82

lukas-lansky opened this issue Nov 7, 2016 · 1 comment

Comments

@lukas-lansky
Copy link

With a following code:

using log4net;
using log4net.Config;
using System;
using System.IO;

namespace EsLog4NetExperiment
{
    class Program
    {
        private static ILog log = LogManager.GetLogger(typeof(Program));

        static void Main(string[] args)
        {
            XmlConfigurator.Configure(new FileInfo("log4netconfig.xml"));

            while (true)
            {
                for (var i = 0; i < 10000; i++)
                {
                    log.Info("Test insert");
                }

                Console.WriteLine($"{DateTime.Now} 10000 logs inserted into ES");
            }
        }
    }
}

... and following configuration:

<?xml version="1.0" encoding="utf-8" ?>
<log4net>
  <appender name="ElasticSearchAppender" type="log4net.ElasticSearch.ElasticSearchAppender, log4net.ElasticSearch">
    <layout type="log4net.Layout.PatternLayout,log4net">
      <param name="ConversionPattern" value="%d{ABSOLUTE} InternalTrackingId[%property{InternalTrackingId}] ExternalTrackingId[%property{ExternalTrackingId}] %-5p %c{1}:%L - %m%n" />
    </layout>
    <connectionString value="Server=135.128.223.112;Index=elasticlogtest;Port=9200;Rolling=true" />
    <lossy value="false" />
    <bufferSize value="100" />
    <evaluator type="log4net.Core.LevelEvaluator">
      <threshold value="DEBUG"/>
    </evaluator>
    <Fix value="0" />
  </appender>

  <root>
    <level value="DEBUG" />
    <appender-ref ref="ElasticSearchAppender" />
  </root>
</log4net>

... and following packages:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="log4net" version="2.0.5" targetFramework="net46" />
  <package id="log4net.ElasticSearch" version="2.3.3" targetFramework="net46" />
</packages>

Running this against non-responsing ElasticSearch (or non-existent, you can run configuration without modification to see this) causes following memory leak:

heap

Making ES instance available will not result in freeing the memory. The same thing happen with or without debugger attached, in debug or release mode, with ES non-existent or crippled by out of memory condition.

I'll try to continue with more research or fix in the evening.

@michal-duda
Copy link

michal-duda commented Nov 12, 2016

I am also looking into this issue and as it is visible from @lukas-lansky screenshot problem is that lib is registering non limited number callbacks for QueueUserWorkItem in appender.

protected virtual bool TryAsyncSend(IEnumerable<LoggingEvent> events)
{
        return ThreadPool.QueueUserWorkItem(SendBufferCallback, logEvent.CreateMany(events));
 }

Most straight forward solution is to use circuit breaker (as also @lukas-lansky proposed). I tried to quickly add Polly circuit breaker into Repository like this:

private static Policy policy = Policy.Handle<Exception>()
        .CircuitBreaker(2, TimeSpan.FromMinutes(1));

    public void Add(IEnumerable<logEvent> logEvents, int bufferSize)
    {
        policy.Execute(() =>
        {
                if (bufferSize <= 1)
                {
                    // Post the logEvents one at a time throught the ES insert API
                    logEvents.Do(logEvent => httpClient.Post(uri, logEvent));
                }
                else
                {
                    // Post the logEvents all at once using the ES _bulk API
                    httpClient.PostBulk(uri, logEvents);
                }
        });
    }

With this in place memory footprint stays on some constant value after two timeouts:

image

Also I think there should be some configurable timeout for http requests, .net HttpClient class should be used instead of WebRequest and number of queued background tasks should be limited 1.

@jptoto What are your thoughts on this? I am good to continue work on this more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants