0

I have an Asp.NET Web API 2. My client calls a particular action method but I need someway to set the timeout value.

I need the timeout value because I want the client to take appropriate action if the action method hasn't returned anything back in say 40 seconds. (Basically that's an arbitrary limit I've chosen...so if it hasn't completed it's job..i.e. hasn't returned back the valid JSON in 40 seconds, we're going to have to assume that something is taking way too long on Azure and we're going to perform a rollback).

Also, if the timeout has occurred I want someone way to Rollback the transaction.

If it helps, I'm using the UnitOfWorkActionFilter along with NHibernate.

The controller and action method are both asynchronous, and I'm explicitly catching the ExecuteAsync method along with the CancellationToken variable.

However, I'm unaware of a way to cancel this call OR use the CancellationToken variable.

I can post code if necessary.

I did read in a few places that since WebApi2 is asynchronous that I may not be able to cancel this!

Any recommendations on how to go about solving this?

2 Answers 2

1

I think setting a timeout on the request is the wrong approach as you will have no visibility of what is going on during the 40 seconds.

Rather make a ajax web request and then subsequent web requests to see if the process has completed.

For example,

  1. Queue the request somehow with the initial request.
  2. Write something to pick up and process the item from the queue. This also means if something goes wrong, you can just roll back at this point. You also need to store the status of the item somewhere.
  3. Write a periodic poll in Javascript that makes another ajax request every 5 seconds to see if the request has been processed or not.
Sign up to request clarification or add additional context in comments.

2 Comments

This sounds like a plausible solution. However, the client in this case is actually a WinForm and not a browser. That's the issue I'm facing here, also I'm a bit stuck with .NET framework 4. Any other way?
If it's a form application, spin off a thread instead. You can then either just attach an event to the thread completing or have a timer set up to check periodically if it's done.
1

Depending on what kind of method is running on your WebApi service you could try the following:

  1. Start a StopWatch at the start of your action
  2. Check periodically if the elapsed time is greater than your arbitrary limit. When that happens throw an Exception (I called mine CalculationTimeLimitExceededException)
  3. Catch the exception and perform a rollback (assuming you want to do a rollback on the server)
  4. Return a response (e.g. HTTP 500 with some useful information, e.g. server timeout)
  5. Since the client gets a response within your time limit you can then handle the error on the client side.

Update (added code for PerformanceWatcher):

public class PerformanceWatcher : IDisposable
{
    private System.Diagnostics.Stopwatch _sw;
    private Timer _timer;
    private int _maxSeconds;

    public bool TimeLimitExceeded { get; private set; }

    public PerformanceWatcher(int maxSeconds)
    {
        _maxSeconds = maxSeconds;
        // start the StopWatch
        _sw = System.Diagnostics.Stopwatch.StartNew();
        // check every second
        _timer = new Timer(1000);
        _timer.AutoReset = true;

        // set event-handler
        _timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
        _timer.Enabled = true;
    }

    private void OnTimedEvent(object source, ElapsedEventArgs e)
    {
        // check if time limit was reached
        if (this._sw.Elapsed.TotalSeconds > _maxSeconds)
        {
            this.TimeLimitExceeded = true;
        }
    }

    public void Dispose()
    {
        this._timer.Dispose();
    }
}

You can use this class in your action:

[HttpGet]
public HttpResponseMessage GetResultFromLongRunningMethod()
{
    using (PerformanceWatcher watcher = new PerformanceWatcher(10))
    {
        try
        {
            // begin long-running operation
            for (int i = 0; i < 20; i++) 
            {
                if (watcher.TimeLimitExceeded) 
                {
                    throw new TimeLimitExceededException();
                }
                Thread.Sleep(1000);
            }
            // end long-running operation
        } catch (TimeLimitExceededException e)
        {
            return this.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Time limit exceeded");
        }
    }
    return this.Request.CreateResponse(HttpStatusCode.OK, "everything ok");
}

The above code isn't tested; I just copied some elements from my own classes.

Update 2: Fixed a bug in the code (Exception thrown from event handler wasn't caught)

3 Comments

Yeah, this was going to be my initial implementation but the idea of the whole stopwatch sitting in an action method didn't really sit well. Maybe because it's a WebApi. Is there any downside to this type of implementation?
@RahulKishore The StopWatch itself isn't costly compared to a long-running task that takes several seconds or more. I've used this for a while in our code (a backtracking algorithm for a best-rate calculation with variable rates and periods) and it works quite well. If you don't want the StopWatch in your cation you can put it in an extra class and inject that into your long-running method. I'll modify my answer.
I've changed the design of my action method. However, this is a good idea now that I see you've injected this. I'll keep this for future reference. Thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.