0

I am using SignalR 1.2.1 (because the project we are implementing in has .NET 4.0).

The process is basically:

  • JS client starts hub connection, and starts by calling BeginPolling:
  • BeginPolling references ReportProgress/GetProgress in RenewalImportProgress.
  • showProgress displays the updated progress message to the client
  • importDone is another client method that stops the connection, shows "Done!", and re-enables the form fields.

The problem that I'm having is that if I try to start a second import, or even clicking a button that does a simple window.location redirect (right after completion) I am delayed for about 5-10 seconds. Other users have reported delays of up to 30 seconds on other machines as well.

I have turned on client-side logging and also have a stateChanged function for signalR and the connection says that it is closed, not sure what may be causing this delay/locking? Happens in both IE and Chrome.

var progressNotifier = $.connection.progressHub;

progressNotifier.client.showProgress = function (message) {
    UpdateProgress(message);
};
progressNotifier.client.importDone = function () {
    $.connection.hub.stop(); //stop hub connection

    //delayed to show "Done!" temporarily
    setTimeout(function () {
        $('#message').hide();
        $('input').prop('disabled', false); //enable input fields
        $('#batchName').focus(); //give focus and clear fields
        $('#batchName').val('');
        $('#batchDescription').val('');
    }, 1000);

};

$.connection.hub.start().done(function () {
    $.ajax({
        url: "Import/ImportXmlFile",
        type: 'POST',
        dataType: "json",
        data: JSON.stringify(@Html.Raw(Json.Encode(Model))),
        contentType: 'application/json;charset=utf-8',
    });

    progressNotifier.server.beginPolling("@Model.BATCHID");
});

function UpdateProgress(msg) {
    $("#result").html(msg);
}

ProgressHub.cs:

public class ProgressHub : Hub
{
    private readonly RenewalImportProgress _renewalImportProgress;

    public ProgressHub() : this(RenewalImportProgress.Instance) { }

    public ProgressHub(RenewalImportProgress renewalImportProgress)
    {
        _renewalImportProgress = renewalImportProgress;
    }

    public void BeginPolling(string batchId)
    {
        _renewalImportProgress.GetProgress(batchId);
    }
}

RenewalImportProgress.cs:

public class RenewalImportProgress
{
    private readonly static Lazy<RenewalImportProgress> _instance = new Lazy<RenewalImportProgress>(() => new RenewalImportProgress(GlobalHost.ConnectionManager.GetHubContext<ProgressHub>().Clients));
    private readonly TimeSpan _updateInterval = TimeSpan.FromMilliseconds(1000);
    private readonly Timer _timer;
    private string _batchId;

    private RenewalImportProgress(IHubConnectionContext clients)
    {
        Clients = clients;
        _timer = new Timer(ReportProgress, null, _updateInterval, _updateInterval);

    }
    public static RenewalImportProgress Instance{ get { return _instance.Value; } }
    private IHubConnectionContext Clients { get; set; }

    private void ReportProgress(object state)
    {
        GetProgress(_batchId);
    }

    public void GetProgress(string batchId)
    {
        if (!string.IsNullOrEmpty(batchId)) _batchId = batchId;

        Stats stats;
        var message = "Preparing file...";
        using (var _repository = new ImportRepository())
        {
            stats = _repository.GetImportStats(_batchId);
        }

        if (stats != null && stats.ProcessCount != 0)
        {
            var processedCount = stats.ProcessCount + stats.ErrorCount;
            if (stats.BatchCount > processedCount)
            {
                message = "Processing... " + processedCount.ToString() + " of " + stats.BatchCount.ToString() + " completed.";
                Clients.All.showProgress(message);
            }
            else if (stats.BatchCount == processedCount)
            {
                message = "Processing file... Done!";
                Clients.All.showProgress(message);
                Clients.All.importDone();
            }
        }
    }
}
Goose
  • 3,241
  • 4
  • 25
  • 37
  • Let's see if I get it. Does this happen after every postback? (you mentioned window.location) –  Apr 30 '14 at 18:36
  • Is `Timer` actually `System.Threading.Timer` ? – thepirat000 Apr 30 '14 at 18:36
  • @Sphaso I upload a file by posting a form, after postback I am returned to the form with additional Model values, which is what initially triggers the JS above. Once the polling begins, the fields/buttons on the form are just disabled while progress of the import (several sprocs inserting data from the uploaded file) is displayed. Once it is complete, `importDone` is called and the fields are enabled once again. One of the buttons redirects the user to another page, this is an example of where the delay happens, but really happens no matter what the next action is. @thepirat000 Yes. – Goose Apr 30 '14 at 18:40

1 Answers1

0

In your code, if your ReportProgress method take more than one second to execute, the timer will execute the ReportProgress again in another thread, at the same time.

Don't know if this can be causing the problem, but it worth trying to change the timer in order to avoid being called again until the current execution finishes:

private RenewalImportProgress(IHubConnectionContext clients)
{
   Clients = clients;
   _timer = new Timer(ReportProgress, null, _updateInterval, Timeout.Infinite);
}

private void ReportProgress(object state)
{
    GetProgress(_batchId);
    _timer.Change(_updateInterval, Timeout.Infinite);
}

System.Threading.Timer in C# it seems to be not working. It runs very fast every 3 second

Community
  • 1
  • 1
thepirat000
  • 12,362
  • 4
  • 46
  • 72
  • Thanks for the suggestion. I can certainly change this to help with possible future issues, but unfortunately doesn't fix the immediate problem. – Goose Apr 30 '14 at 19:11