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

Hanging / Freezes when rebuilding solution on IIS #56

Closed
appstractmeyn opened this issue Jan 5, 2015 · 25 comments
Closed

Hanging / Freezes when rebuilding solution on IIS #56

appstractmeyn opened this issue Jan 5, 2015 · 25 comments

Comments

@appstractmeyn
Copy link

I'm using the latest version of the library (2.0.0)

The following code works fine.

var document = new HtmlToPdfDocument
            {
                GlobalSettings =
                {
                    ........
                },
                Objects = {
                    new ObjectSettings { PageUrl = "https://www.fb.com" }
                }
            };

            return File(pdfConverter.Convert(document), "application/pdf");

But, after I change the code, for example if I made the url google.com instead of fb.com, IIS/IIS_Express freezes and does not generate the pdf. The only way to resolve this is to restart the web server. Any idea, how I could get rid of this situation??

@tuespetre
Copy link
Owner

What were you passing in before?

@appstractmeyn
Copy link
Author

new StaticDeployment(HttpRuntime.AppDomainAppPath + "/wkhtml"))

@appstractmeyn
Copy link
Author

Any better solution? Not sure why I need a brand new copy each time.

@tuespetre tuespetre reopened this Jan 5, 2015
@tuespetre
Copy link
Owner

Wait a minute -- you are using the RemotingToolset, right?

@appstractmeyn
Copy link
Author

static string tpath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString(), "wkhtml");
private static IConverter pdfConverter = new ThreadSafeConverter(new RemotingToolset<PdfToolset>(new Win32EmbeddedDeployment(new StaticDeployment(tpath))));

@tuespetre
Copy link
Owner

I've been able to reproduce the issue and correct it. I'll release a patch version tonight.

The issue was only surfacing when converting from URL instead of just raw HTML; when the AppDomain unloads the RemotingConverter was not invoking wkhtmltopdf_deinit which must have left some unmanaged web loading object hanging before trying to unload the AppDomain. The temporary workaround is a bit hacky but it stays underneath the surface of the public API. There will now be a unit test for this scenario as well -- converting from URL after AppDomain unloading.

@appstractmeyn
Copy link
Author

Thanks a lot.

@timdooling
Copy link

I tried the new changes, and the file is still locked when I try to check-in new changes on TFS, but fortunately when the build crashes, I simply restart it and it works fine.

@tuespetre
Copy link
Owner

If you had an existing one running with the unpatched version, that would probably happen.

  • Derek Gray

On Jan 6, 2015, at 12:04 PM, timdooling notifications@github.com wrote:

I tried the new changes, and the file is still locked when I try to check-in new changes on TFS, but fortunately when the build crashes, I simply restart it and it works fine.


Reply to this email directly or view it on GitHub.

@wc-matteo
Copy link

I have a similar issue with the latest version (v2.0.1). I am calling convert from two IIS hosted web applications (vb.net Web Forms).

My environment:

• One AppPool
• Two web sites running from different folders but having the same code and AppPool (it is a custom CMS)
• TuesPechkin wrapped in a class (acting as a thin wrapper) and included as a reference in the web application projects (using TuesPechkin.Wkhtmltox.Win64 too)

The pdfs are generated from a URL. Trying to generate a pdf from one of the two sites works (no matter which site or how many times in a row). After the first generation though, any attempt to generate a pdf from the other site completely locks the AppPool (requires killing the process).

Converter is a static (shared in vb.net) field. I am using IIS 8.5 with a 64 bit AppPool (Enable 32-Bit Applications = False). This is how I create the converter:

Converter = New ThreadSafeConverter(
New RemotingToolset(Of PdfToolset)(
New Win64EmbeddedDeployment(
New StaticDeployment(
Path.Combine(Path.GetTempPath(), "mycustomcms")
)
)
)
)

ANOTHER THING...
...is that the wkhtmltox dll is not copied in the temp folder (and mycustomcms is not created either).

--Matteo

@tuespetre
Copy link
Owner

If you must have both sites in one app pool (ie one process) then each site will need to point to a unique deployment of wkhtmltox.dll.

  • Derek Gray

On Jan 16, 2015, at 9:18 AM, wc-matteo notifications@github.com wrote:

I have a similar issue with the latest version (v2.0.1). I am calling convert from two IIS hosted web applications (vb.net Web Forms).

My environment:

• One AppPool
• Two web sites running from different folders but having the same code and AppPool (it is a custom CMS)
• TuesPechkin wrapped in a class (acting as a thin wrapper) and included as a reference in the web application projects (using TuesPechkin.Wkhtmltox.Win64 too)

The pdfs are generated from a URL. Trying to generate a pdf from one of the two sites works (no matter which site or how many times in a row). After the first generation though, any attempt to generate a pdf from the other site completely locks the AppPool (requires killing the process).

Converter is a static (shared in vb.net) field. I am using IIS 8.5 with a 64 bit AppPool (Enable 32-Bit Applications = False). This is how I create the converter:

Converter = New ThreadSafeConverter(
New RemotingToolset(Of PdfToolset)(
New Win64EmbeddedDeployment(
New StaticDeployment(
Path.Combine(Path.GetTempPath(), "mycustomcms")
)
)
)
)

Matteo

Reply to this email directly or view it on GitHub.

@wc-matteo
Copy link

And giving the StaticDeployment constructor a path with a random string (or a guid) in it could accomplish that (removing the static modifier)? The issue that the dll doesn't get copied in the temp folder remains however... it is copied over in the web applications bin folder though (TuesPechkin.Wkhtmltox.Win64.dll)

@tuespetre
Copy link
Owner

The temp folder will vary by the user your app pool runs under, so be sure you are checking the right one. It could be C:\Windows\Temp or C:\Users\<some_user_name>\AppData\Local\Temp depending on how you have things set up.

With 2.1.0 there will be an included TempFolderDeployment class that somewhat imitates the deployment behavior of 1.0.3. I would recommend switching to use that as soon as 2.1.0 is released. You can use the same method right now if you want, it pretty much does this:

            Path = System.IO.Path.Combine(
                System.IO.Path.GetTempPath(),
                AppDomain.CurrentDomain.BaseDirectory.GetHashCode().ToString());

Assuming each of your sites have different base directories, that should work for you.

@wc-matteo
Copy link

>The temp folder will vary by the user your app pool runs under
Duh... so obvious! -_-

Now it's working, but after having generated at least one pdf from each site, it hangs. It must be that static modifier.

Is there a really good reason not to want to instantiate the converter multiple times? I mean, is it a matter of performance or...? And do you have any advice for a .net facility that persist data per site (and not per AppPool as the static modifier does)?

@tuespetre
Copy link
Owner

The reason we don't want to instantiate a converter multiple times is because to do so, we would have to load a unique copy of wkhtmltox.dll into memory for each one. This is due to the nature of unmanaged libraries.

Now, an application pool is a process, so by having multiple sites on one pool, they share that process. That means a single unique wkhtmltox.dll file would be loaded only one time for the process. However, each site will run within its own AppDomain, meaning that your static variable in managed code is not the going to share the same reference between your sites.

So long story short, did you change it to use a random path or the base directory-scope solution I posted above? As long as each site touches a unique copy of the wkhtmltox.dll file, everything should be alright.

@timdooling
Copy link

I am using the converter in a web service run on IIS.

What I have done is placed a complete, unzipped copy of the "Wkhtmltox.dll" file at the root directory of my web service, and I branch off that to the .dll file using:

    Shared Property converter As IConverter = Nothing

         ...
        Dim physicalApplicationPath As String = MyClass.Context.Request.PhysicalApplicationPath

           If IsNothing(converter) Then
                converter = New ThreadSafeConverter(
     New RemotingToolset(Of PdfToolset)(
     New StaticDeployment(physicalApplicationPath)))
            End If

    ...

            converter = Nothing ' releases the memory to garbage collection

This approach generates pdfs without hanging every time.

The only problem I have is that TFS build fails and I have to retry it when I check in code changes, a minor annoyance, because the file is locked. The first build attempt apparently unlocks it.

I surround my calls to the web service print routine with Using (service) ... End Using.

Apparently between calls the "Wkhtmltox.dll" file is locked, but that doesn't seem to affect whether the system can use it using the method I am using.

I pass a url to the routines to let .NET do all the rendering so I don't have to reinvent the Render subroutine.

I get back a WYSIWYG response as long as I don't have any rendering that depends on javascript.

@wc-matteo
Copy link

Sorry for the delayed response.

>each site will run within its own AppDomain
Ah! I was conflating AppPools and AppDomains; now I see...

In my last response I was using your base directory-scope solution. Now I don't seem to be able to get the other site working, not even for one generation (after the other site generation)... I'm back where I started...

This is the converter, as of now:

Converter = New ThreadSafeConverter(
  New RemotingToolset(Of PdfToolset)(
    New Win64EmbeddedDeployment(
      New StaticDeployment(
        Path.Combine(
          Path.GetTempPath(),
          AppDomain.CurrentDomain.BaseDirectory.GetHashCode().ToString())))))

I have checked the temp folder (it's the Windows one, by the way) and the converter successfully creates a directory for each site.

UPDATE

I tried with a simple generic handler (both with iis express and iis). The first generation always succeeds. Every other generation stalls the worker process, which requires to be killed.

Am I doing something wrong?

Nuget packages:

  • TuesPechkin 2.0.1
  • TuesPechkin.Wkhtmltox.Win64 0.12.1
Imports TuesPechkin
Imports System.IO

Public Class Handler1
    Implements System.Web.IHttpHandler

    Private Shared Converter As IConverter

    Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
        If context.Request.QueryString.Item("url") = Nothing Then Exit Sub

        context.Response.ContentType = "application/pdf"

        Dim Document As New HtmlToPdfDocument
        Document.Objects.Add(New ObjectSettings With {
            .PageUrl = context.Request.QueryString.Item("url")
        })

        Converter = New ThreadSafeConverter(
            New RemotingToolset(Of PdfToolset)(
                New Win64EmbeddedDeployment(
                    New StaticDeployment(
                        Path.Combine(
                            Path.GetTempPath(),
                            AppDomain.CurrentDomain.BaseDirectory.GetHashCode().ToString())))))

        Dim Stream As New MemoryStream(Converter.Convert(Document))
        Stream.WriteTo(context.Response.OutputStream)
    End Sub

    ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
        Get
            Return False
        End Get
    End Property
End Class

@tuespetre
Copy link
Owner

My apologies @wc-matteo, it does appear that there are some issues when using multiple wkhtmltox.dll files in a single process, and I think it's going to be related to how P/Invoke binds to the native modules.

Looks like I need to start planning for 2.2.0 -- deprecating the WkhtmltoxBindings class and beginning to use dynamically obtained unmanaged delegates. Fun!

@wc-matteo
Copy link

Dynamically obtained unmanaged delegates do sound like fun! (whatever they are :P)

But, correct me if I'm wrong, in the generic handler case you're not really using multiple wkhtmltox.dll, are you?

And talking about the generic handler, version 2.1.0 does not work (didn't test with the cms):

[DllNotFoundException: Unable to load DLL 'wkhtmltox.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E).]
   TuesPechkin.ThreadSafeConverter.Invoke(FuncShim`1 delegate) +246

@tuespetre
Copy link
Owner

Sorry, I suppose I did not look at your code sample closely enough. You are instantiating a new instance of the converter with each request (in the ProcessRequest method.) You will want to surround that with either a null check, or move that to a static constructor, or if you can in VB.NET, declare it inline with your static field.

@wc-matteo
Copy link

Well, you learn something new every day... I thought the compiler automatically did the null check for me with a static field...

It still can't find the dll, though. Can you reproduce it or should I check more thoroughly on my end?

P.s. Could you give me an example of an inline declaration (using C#)?

UPDATE

I can confirm that just updating from 2.0.1 to 2.1.0 generates the error above (tried both with StaticDeployment and the new TempFolderDeployment).

@tuespetre
Copy link
Owner

I've just pushed 2.1.1 which address the issue you've discovered.

@wc-matteo
Copy link

Thank you for the promptly fix! :)

For a resolution to my original issue I need to wait for 2.2.0 then?

About the TempFolderDeployment: If it copys the dll always in the same folder (per site), when there's a software update to the package, you need to clear the dll from the temp folder, right?

@tuespetre
Copy link
Owner

I've actually got the assembly version in the path of the embedded deployment since 2.1.1/0.12.2.1, so you shouldn't have to worry about manually deleting anything.

@GaryWang1
Copy link

I have the same issue. I just get the latest version TuesPechkin 2.1.1.0 and TuesPechkin.Wkhtmltox.Win32.dll 0.12.2.1. It works very good on converting html to pdf. The only issue is every time the aspx or aspx.vb file changed and cause recompile, we have to recycle the application pool, otherwise the whole site will freeze.

My environment is a simple web site, just aspx file and aspx.vb file. For testing it only has one page,

I tried all different ways, like, TempFolderDeployment, StaticDeployment, see code below

Public Shared ReadOnly Property convertidor() As IConverter
    Get
        If _converter Is Nothing Then
            SyncLock _PadLock
                If _converter Is Nothing Then
                    'Dim wkhtmltoxPath As String = "C:\Windows\Temp\80570420\4\0.12.2.1"
                    'Dim physicalApplicationPath As String = "C:\inetpub\wwwroot\MySiteRoot"
                    _converter = New ThreadSafeConverter(New RemotingToolset(Of PdfToolset)(New Win32EmbeddedDeployment(New TempFolderDeployment())))
                    '_converter = New ThreadSafeConverter(New RemotingToolset(Of PdfToolset)(New Win32EmbeddedDeployment(New StaticDeployment(physicalApplicationPath))))
                End If
            End SyncLock
        End If
        Return _converter
    End Get
End Property

Any idea to get rid of the recycle application pool when code changes.

Thank you,

Gary

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

No branches or pull requests

5 participants