tuespetre / TuesPechkin

A .NET wrapper for the wkhtmltopdf library with an object-oriented API.
326 stars 116 forks source link

IIS worker process keeps hogging memmory and crashes eventually leaving un-recycled app pool to cause 500 error #152

Open wickstargazer opened 7 years ago

wickstargazer commented 7 years ago

Hi,

I think the subject does explain the root cause of the problem. My implementation is

static string tpath = Path.Combine(HttpContext.Current.Server.MapPath("~/wkhtmltox/"), Guid.NewGuid().ToString(), "MYPDFAPP_WKHTMLTOX");
static object _padLock;
static IConverter _converter;
        static IConverter Converter
        {
            get
            {

                if (_converter == null)
                {
                    lock(_padlock){
                        if(_converter == null)
                             _converter = new ThreadSafeConverter(new RemotingToolset<PdfToolset>(new Win64EmbeddedDeployment(new StaticDeployment(tpath))));
                    }
                }
                return _converter;
            }
        }

I also have the loaduserprofile in app pool set to true which helps only to prolong the time from 0 to crash (eating up around 80% of Ram then crashes)

Please help, this service is absolute neccessary other wise i have to move on to another solution..

Regards

alin-prisecaru commented 7 years ago

Hi wickstargazer,

I've been using this plugin for over 2 years - works nicely even with 1000+ pages of PDF generated from HTML. I use these packages: package id="TuesPechkin" version="2.1.1" targetFramework="net45" package id="TuesPechkin.Wkhtmltox.Win64" version="0.12.2.1" targetFramework="net45"

Also, I had to make this fix a while ago: In Visual Studio, I opened up Tools -> Options -> Projects and Solutions -> Web Projects and I had to make sure that the option called "Use the 64 bit version of IIS Express for web sites and projects" is checked.

Implementation: Create a static class in your application:

public static class TuesPechkinInitializerService
    {
        private static string staticDeploymentPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wkhtmltopdf");

        public static void CreateWkhtmltopdfPath()
        {
            if (Directory.Exists(staticDeploymentPath) == false)
            {
                Directory.CreateDirectory(staticDeploymentPath);
            }
        }

        public static IConverter converter = 
            new ThreadSafeConverter(
                new RemotingToolset<PdfToolset>(
                    new Win64EmbeddedDeployment(
                        new StaticDeployment(staticDeploymentPath)
                    )
                )
            );
    }

In GLOBAL.asax, I initialize that class on project start:

TuesPechkinInitializerService.CreateWkhtmltopdfPath();

And to use it:

HtmlToPdfDocument pdfDocument = new HtmlToPdfDocument
                {
                    GlobalSettings = new GlobalSettings(),
                    Objects =
                    {
                        new ObjectSettings
                        { 
                            ProduceLocalLinks = true,
                            ProduceForms = true,
                            HtmlText = htmlContent
                        }                    
                    }
                };

                byte[] pdfDocumentData = TuesPechkinInitializerService.converter.Convert(pdfDocument);

Hope this helps somehow. My server resources are also a bit strained when a HTML to PDF conversion is done, but in the end it cleans itself up.

By looking at your implementation, I can see that the class is initialized every time it's used - I don't think that's a good approach for this interface because it was made for desktop applications and it's purpose is to be initialized only one time per application instance.

wickstargazer commented 7 years ago

Hi my PDFUtil class is static ofcourse, so is the variables they are declared only once.

I tried shuffling things around, making its caller static as well or not static but what i find is this...

byte[] pdfBuf = TuesPechkinService.Converter.Convert(document);

This line .... causes the converter to eat up memory i think it is because it is converting the file inside the memory stream itself and the issue it, that memory is never returned.

Everything works fine as i said before. Its just that this memory when not returned by converter or dispose or unload whatever we want to call it. It crashes the app pool eventually.

Please help!

wickstargazer commented 7 years ago

*Bump

basicvn commented 7 years ago

Hi wickstargazer,

Did you try to unload the Toolset by calling TuesPechkinService.ToolSet.Unload() after Convert action?

I created another property in Service class to reference ToolSet (also static), so I can unload it after convert done.

wickstargazer commented 7 years ago

Hey, I solve the problem temporary by calling GC.Collect()

ghost commented 6 years ago

Hello,

Do you have any updates on this track? Any workarounds to avoid this issue?

Thanks.

sameerAhmad9291 commented 5 years ago

I followed @alin-prisecaru approach and unload convert using TuesPechkinService.ToolSet.Unload(). I did worked for bulk generation of 1000+ pdfs. But it takes around 10 seconds to generate one pdf.

sandeepgosavi commented 5 years ago

I tried all the solutions stated above but, still PROD crashed. I am using pdftoolset and imagetoolset both.

sameerAhmad9291 commented 5 years ago

i also faced issue when i loop through object list which contains pfdFile: byte[] attribute. Make sure you did clear or control your pdfFile attribute.

I solved this by nhibernate lazyloading property.