2

In an ASP.NET Web Forms application I have a parent form, which contains another content page within an IFrame. When user clicks on a link within the content page, a long running process (> 30 min) is started. Upon completion a popup is displayed to the user indicating number of records processed.

I need to prevent session timeout programatically, without changing the default 20 min in Web.config. I have been trying to implement the Heartbeat example posted here (and all over the web, so I know it should work) Keeping ASP.NET Session Open / Alive , but it appears that it's used mostly for idle sessions.

In my case, once the content page request goes to server side and long running process is initiated, the HTTP Handler is not called. When the process completes, all the calls are made immediately one after another like they have been "queued".

Here's my HTTP Handler:

<%@ WebHandler Language="VB" Class="KeepSessionAliveHandler" %>

Imports System
Imports System.Web

Public Class KeepSessionAliveHandler
    Implements IHttpHandler, SessionState.IRequiresSessionState

    Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
        context.Session("heartbeat") = DateTime.Now
        context.Response.AddHeader("Content-Length", "0")

    End Sub

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

End Class

Javascript function in Head element for parent page. Create interval calling the handler every 8 seconds (to be increased to 10 min in production).

function KeepSessionAlive() 
{    
    if (intervalKeepAliveID) 
        clearTimeout(intervalKeepAliveID); 

    intervalKeepAliveID = setInterval(function() 
    {
        $.post("KeepSessionAliveHandler.ashx", null, function() 
        {
            // Empty function
        });
    }, 8000);
}

intervalKeepAliveID is declared in a main Javascript file included in all pages of the application.

This is the code for my onclick event in the content page Head

$(document).ready(function() 
{
    // Ensuring my code is executed before ASP.NET generated script
    $("#oGroup_lnkSubmit_lnkButton").attr("onclick", null).removeAttr("onclick").click(function() 
    {
        // Prevent the browser from running away
        // e.preventDefault();

        window.parent.KeepSessionAlive();              

        // Wave goodbye
        //window.location.href = $(this).attr('href');

        WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions($(this).attr("name"), "", true, "", "", false, false));                    
    });
}); 

Somewhere I read that Javascript runs in a single thread, but given that fact that my repeating interval is outside the content page, I do not believe this should apply here...

Community
  • 1
  • 1
  • What are you using to respond to the user? (Does your page poll for completion? Or are you using a server-side signaling framework?) And, is the long-running process running directly in your server thread? Or do you launch a worker thread? – GalacticCowboy Mar 25 '13 at 16:19
  • 1
    Requests which cause access to the session are blocked while another request may have write access to the session. This is to prevent cropss-threading issues. I've given details in an answer elsewhere, I'll see if I can dig it up – Basic Mar 25 '13 at 16:21
  • No worker thread, it is part of the code behind for onclick event (it is actually a user control that is using a link). I can see my code being hit in Firebug debugger. The page does not poll for completion. It's pretty standard Web Form - "on click do long running process and come back; also show popup" – ChicagoMyKindOfTown Mar 25 '13 at 16:28

2 Answers2

2

It's not an issue with JS being single-threaded - The A of AJAX stands for Asynchronous - eg it doesn't block (even if you tell it to block, it really just preserves state until a response is received)

From this MSDN article...

Access to ASP.NET session state is exclusive per session, which means that if two different users make concurrent requests, access to each separate session is granted concurrently. However, if two concurrent requests are made for the same session (by using the same SessionID value), the first request gets exclusive access to the session information. The second request executes only after the first request is finished. (The second session can also get access if the exclusive lock on the information is freed because the first request exceeds the lock time-out.) If the EnableSessionState value in the @ Page directive is set to ReadOnly, a request for the read-only session information does not result in an exclusive lock on the session data. However, read-only requests for session data might still have to wait for a lock set by a read-write request for session data to clear.

See this page for a more detailed explanation of the problem and a workaround that gives you greater control of how the blocking is implemented and a potential workaround.

Basic
  • 26,321
  • 24
  • 115
  • 201
  • Correct! Thank you for taking the time to understand my long question and for your swift response. – ChicagoMyKindOfTown Mar 25 '13 at 17:54
  • @ChicagoMyKindOfTown You're very welcome - I recognised the problem as I've hit it myself before. Welcome to SO. Hopefully we'll see you around in future. – Basic Mar 25 '13 at 19:00
0

I think you've got a couple of moving parts here that are blocking each other from behaving correctly.

  • Because your process is running in the server thread, this blocks other requests from being processed.
  • Because the keepalive depends on getting a response from the server, it doesn't complete.

I'd suggest that you look into a solution like ASP.NET SignalR, along with spawning the long-running process as a separate thread so that your server can continue to service incoming requests.

GalacticCowboy
  • 11,663
  • 2
  • 41
  • 66