2

The idea here is to run some long running methods concurrently, 5 at a time. When 15 of them are done (3 blocks of 5), I should return from the controller method.

The problem is that it returns too early. Usually it returns after the first block of 5, but I've seen it return mid way through the second block.

^ - I have breakpoints at all the return statements, but these don't get hit. I know it returns because I am calling it from Javascript and have a breakpoint in my browser. The return data is an empty string. Following a suggestion on another question - I added an override void Dispose(..) to see if this was getting hit, but it wasn't. Then I put breakpoints in all the events in my Global.asax.cs, such as in Application_Error, Session_End and Application_End, however these don't get hit in the premature return either. I also added a breakpoint in my ActionFilterAttribute in the OnActionExecuted event, but this doesn't get hit either! I also have elmah installed which usually catches errors. I cannot find where my code is returning to the getJson() call.

The LongRunning methods take about 30 seconds each to run, per item in a block. I can increase (or decrease) the block count from 5 to say 10, and it will still return after 10 complete.

I've trimmed down the code as much as possible to give a clear representation of the issue:

    [HttpGet]
    [Route("TestAPI/RunTest")]
    public async Task<IHttpActionResult> RunTest(bool reload, DateTime period)
    {
        if (reload)
        {
            await BuildNewDataTest(period);
            // Also tried: 
            // await BuildNewDataTest(period).ContinueWith((x) => { results = _myUoW.GetDataTest(period); });;
        }

        return Ok(_myUoW.GetDataTest(period));
        // return Ok(results);
    }
    private async Task<bool> BuildNewDataTest(DateTime period)
    {
        // DoSomeStuff...
        DateTime oldDate = DateTime.Now.AddDays(-30);
        AddStuffToDatabase();
        List<Task> concurrentRuns = new List<Task>();

        for (int i = 0; i < 15; i += 5)
        {
            concurrentRuns.Clear();
            List<MyType> block = fullList
                .Skip(i)
                .Take(5).ToList();

            block.ForEach(x => concurrentRuns.Add(RunStuffAsync(period, oldDate, x.IsOpen)));

            await Task.WhenAll(concurrentRuns);
        }
        return true;
    }

    private async Task<bool> RunStuffAsync(DateTime period, DateTime prevDate, bool isOpen)
    {
        // DoSomeStuff...

        await Task.Run(() =>
        {
            _myUoW.RunSomeLongRunningProcess(longRunningID, period);
            return true;
        });
        return false;
    }
    private void AddStuffToDatabase()
    {
        _myUoW.CreateNewEntry(...);
    }

After my Javascript breakpoint is hit, the code continues to run - so all my 15 processes are run fully and my breakpoint at the return Ok(..) gets hit.

The calling JS is:

$.getJSON('TestAPI/RunTest', { reload: self.reload(), period: self.period() }, function (data) {
    var myData = JSON.parse(data);
    self.myReturnedData(myData);

    createDataTable(myData);
    });

Thanks for any help.

cdsln
  • 830
  • 1
  • 11
  • 33
  • Is `RunSomeLongRunningProcess()` async? – Roman Aug 01 '17 at 09:54
  • Nope - Should it be? – cdsln Aug 01 '17 at 10:02
  • I just changed it to `async` and also changed my call to `await Task.Run(async () => { _myUoW.RunSomeLongRunningProcessAsync(longRunningID, period); return true; }); ` and it still returned early. – cdsln Aug 01 '17 at 10:12
  • AddStuffToDatabase should be also awaitable. – Marek Kwiendacz Aug 01 '17 at 10:27
  • I am not seeing any glaring async issues in the presented code. One complaint though: `block` is a generic `List`, which does not define a field or property called `IsOpen`. – Kirill Shlenskiy Aug 01 '17 at 10:31
  • @MarekKwiendacz All other methods called within my `BuildNewDataTest` method should be awaitable? I have a lot more methods called from within there, some call methods in different classes - I stripped out most of them for brevity. – cdsln Aug 01 '17 at 10:57
  • @KirillShlenskiy `block` is a list of type `MyType` which has an `IsOpen` property! – cdsln Aug 01 '17 at 10:58
  • @cdsln, then what you meant is `x.IsOpen`. – Kirill Shlenskiy Aug 01 '17 at 11:11
  • @KirillShlenskiy I see what you mean now. This is obviously this is a victim of the cut-down version of code I provided. My own code compiles correctly and runs. Fixed now to avoid distracting from real issue. – cdsln Aug 01 '17 at 11:18
  • 1
    Did you check for all the possible timeouts? https://stackoverflow.com/q/2414441/5265292 ; https://stackoverflow.com/q/3829202/5265292 possibly there are even more things involved that can time out. – grek40 Aug 01 '17 at 13:42
  • Can you post the client code? If it is true that `RunTest()` is not reaching it's `return` and that method is still executing then it sounds like the client is not waiting for the result. – JSteward Aug 01 '17 at 15:15
  • Doesn't the way you add `RunStuffAsync` to the list execute the `RunStuffAsync` synchronously? – Dennis Kuypers Aug 01 '17 at 15:39
  • 1
    @grek40 Timeout was the issue - calling my WebAPI controller method using RestSharp. I was sure I would have gotten a timeout error somewhere.. Silly oversight on my part. Thanks - if you want to add an answer I'll accept it. – cdsln Aug 02 '17 at 11:04
  • Nah, I don't know enough details about the different kinds of timeout and after your comment I'm still not really sure which of the timeouts was actually at fault and how to change it to resolve. If anyone feels like writing a decent answer - go ahead. – grek40 Aug 02 '17 at 11:08

0 Answers0