Benchmarking ASP.net Web API Actions - c#

I am working on a ASP.net Web API 2 project and would like to benchmark the time of each controller action. My idea was to use ActionFilterAttribute and add a http header on the response containing the time taken.
Controller:
[Timing]
public class MyController : ApiController
{
[Route("get")]
public IHttpActionResult Get() {
System.Threading.Thread.Sleep(1000);
return Ok();
}
}
ActionFilterAttribute:
public class TimingAttribute : ActionFilterAttribute
{
private System.Diagnostics.Stopwatch timer;
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
timer = System.Diagnostics.Stopwatch.StartNew();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
timer.Stop();
if (actionExecutedContext.Response != null && actionExecutedContext.Response.Content !=null)
{
actionExecutedContext.Response.Content.Headers.TryAddWithoutValidation("Execution-time", timer.ElapsedMilliseconds.ToString());
}
}
}
When I run this, I get execution times of less than 1 second even though I have a 1s delay in the controller. Does anyone know why this is the case or if there is a better way to benchmark Web API 2?

There are lots of things that are executed along the pipeline before OnActionExecuting is hit. Also, there are other things that are executed after OnActionExecuted, the most obvious being the HttpActionResult. Therefore It would be better to use HttpApplication.BeginRequest event to start mesasuring time, and HttpApplication.EndRequest to end measuring time.
However, I think there are better ways to benchmark a web application than log elapsed times at the server side. Fiddler has lots of nice features related to benchmarking:
You can capture the http traffic of interest by using filters.
The statistics view, where you can see lots of statistics of the selected sessions.
The time line view which shows you a nice chart about time taken by selected sessions.
You can save the sessions into a sessions archive, this is a zip file containing all information about the sessions, including xml files with timing information, that you can analize later to build you own reports.

I ran into the same problem, and found that the solution on this blog worked:
public class StopwatchAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
base.OnActionExecuting(actionContext);
actionContext.Request.Properties[actionContext.ActionDescriptor.ActionName] = Stopwatch.StartNew();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
Stopwatch stopwatch = (Stopwatch)actionExecutedContext.Request.Properties[actionExecutedContext.ActionContext.ActionDescriptor.ActionName];
if (actionExecutedContext.Response != null && actionExecutedContext.Response.Content != null)
{
actionExecutedContext.Response.Content.Headers.TryAddWithoutValidation("Execution-Time", stopwatch.ElapsedMilliseconds.ToString());
}
}
}

Related

How to count await method execution time

I am working on a dot net core project. In which I have to count how much had taken by my await method while executing.
[HttpPost]
public async Task<bool> InsertIntoDatabase([Frombody] StudentModel objStu) {
//For Each method and other stuff
await DoInsert(Fiels Details);
}
I am calling this method in Ajax. So after successful execution of code, I want to return the number of times in minutes taken by the method.
This insertion process contains 500+ records. So, I am interested to calculate the time
One approach is to use Stopwatch
[HttpPost]
public async Task<bool> InsertIntoDatabase([Frombody] StudentModel student)
{
var clock = new Stopwatch();
clock.Start();
await DoInsert(student);
clock.Stop();
var minutes = clock.Elapsed.TotalMinutes();
}
Stopwatch Class
Why not return a custom object in your task with the time and the result?
private async Task<MyClass> GetResult()
{}
public class MyClass
{
public bool Success { get; set; }
public long TimeInSeconds { get; set; }
}
I would highly recommend checking out MiniProfiler.
It's extremely easy to setup!
Install MiniProfiler via Nuget
Add the MiniProfiler tag helper to your _Layout.cshtml
Add the MiniProfiler tag (<mini-profiler />) to your HTML (I put mine in Layout.cshtml as well)
Add to services in Startup.cs:
services.AddMiniProfiler(options =>
{
options.RouteBasePath = "/profiler";
options.SqlFormatter = new SqlServerFormatter();
});
Add this AFTER calling app.UseStaticFiles():
if (env.IsDevelopment())
{
app.UseMiniProfiler();
}
Now MiniProfiler is ready to go and will only appear in development (step 5). To profile some code do:
using (MiniProfiler.Current.Step("This Will be the label displayed on the front end"))
{
//code you want to profile
}
Now run your code and voila! MiniProfiler will put a very small table on your page (I THINK it defaults top left). Each row is the elapsed of the request (works with AJAX too!).

Entry point of WebAPI every time it is accessed

I want to identify the one point which is hit every time before a request goes to the controller in the webAPI. I need to put in a custom authentication at that point. I am already doing a customAuthorization but I want to tweak in some custom authentication even before it reaches the controller.
The application_Start method only gets triggered once and so I am not quite sure what is the one place where the control goes every time we put in a URL in the browser and hit enter.
Gloabal.asax has more methods, which can be overloaded and one of them is Application_BeginRequest
And here's more detailed lifecycle. Controller factory also might help you intercepting and tweeking requests.
protected void Application_BeginRequest(object sender, EventArgs e) //Not triggered with PUT
{
//your code
}
You can opt for ActionFilterAttribute of Web API. This is triggered for every request that comes in.
Execution pipeline:
Controller Constructor > ActionFilter's OnActionExecuting > Controller action > ActionFilter's OnActionExecuted
Simple ActionFilterAttribute implementation:
public class YourFilterName : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
// pre-processing
//Your authentication logic goes here - use actionContext
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var objectContent = actionExecutedContext.Response.Content as ObjectContent;
if (objectContent != null)
{
var type = objectContent.ObjectType; //type of the returned object
var value = objectContent.Value; //holding the returned value
}
Debug.WriteLine("OnActionExecuted Response " + actionExecutedContext.Response.StatusCode.ToString());
}
}

Override global action filter in controller/action in ASP.NET Core MVC 1.0.1 (ASP.NET Core 1.1)

I am building a ASP.NET Core MVC application and am trying to create a global action filter that logs how much time is spent executing an action (it should only log if spent time is above some threshold). I have succesfully done this but now I want to be able to say that a single action or a single controller should have a different threshold. When I try this, my action filter is applied twice(which is not what I want) but with the correct two different thresholds.
I have tried quite a few things and searched around. In an MVC 3 and an MVC 4 project I have successfully done this using RegisterGlobalFilters() in Global.asax and it automatically overrides the global one when I used the attribute on a controller/action. I have also tried the approach listed in this post, without luck:
Override global authorize filter in ASP.NET Core MVC 1.0
My code for my ActionFilterAttribute:
public class PerformanceLoggingAttribute : ActionFilterAttribute
{
public int ExpectedMax = -1; // Log everything unless this is explicitly set
private Stopwatch sw;
public override void OnActionExecuting(ActionExecutingContext context)
{
sw = Stopwatch.StartNew();
}
public override void OnActionExecuted(ActionExecutedContext context)
{
sw.Stop();
if (sw.ElapsedMilliseconds >= ExpectedMax)
{
// Log here
}
}
//public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
//{
// // If there is another performance filter, do nothing
// if (context.Filters.Any(item => item is PerformanceLoggingAttribute && item != this))
// {
// return Task.FromResult(0);
// }
// return base.OnActionExecutionAsync(context, next);
//}
}
I am applying this global filter in my Startup.cs:
services.AddMvc(options =>
{
if (_env.IsProduction()) options.Filters.Add(new RequireHttpsAttribute());
//options.Filters.Add(new PerformanceLoggingFilter() { ExpectedMax = 1 }); // Add Performance Logging filter
options.Filters.Add(new PerformanceLoggingAttribute() { ExpectedMax = 1 }); // Add Performance Logging filter
});
And in my controller I am applying the attribute:
//[TypeFilter(typeof(PerformanceLoggingFilter))]
[PerformanceLogging(ExpectedMax = 2)]
public IActionResult Index()
{
var vm = _performanceBuilder.BuildPerformanceViewModel();
return View(vm);
}
As you can tell from the code snippets above I have tried the OnActionExecutionAsync approach and I have also tried a IActionFilter instead and using [TypeFilter(typeof(PerformanceLoggingFilter))] on actions, but no luck.
Can anyone help me out?
May suggest you a bit different implementation of what you try to achieve by using one action filter and additional custom attribute:
create a new simple attribute (let's name it ExpectedMaxAttribute), that just holds the ExpectedMax value. Apply this attribute to controller's actions with different values.
keep your PerformanceLogging action filter as global, but modify implementation. On OnActionExecuted method check if controller's action has ExpectedMaxAttribute. If yes, then read ExpectedMax value from attribute, otherwise use the default value from the action filter.
Also, I recommend you to rename action filter accordingly to convention naming something like PerformanceLoggingActionFilter.
I got it working thanks to #Set's answer above in combination with this answer:
https://stackoverflow.com/a/36932793/5762645
I ended up with a global action that is applied to all actions and then having a simple ExpectedMaxAttribute that I put on actions where the threshold should be different. In the OnActionExecuted of my global action filter, I then check if the action in question has the ExpectedMaxAttribute attached to it and then read the ExpectedMax from that. Below is my attribute:
public class PerformanceLoggingExpectedMaxAttribute : ActionFilterAttribute
{
public int ExpectedMax = -1;
}
And the OnActionExecuted part that I added to my ActionFilter:
public override void OnActionExecuted(ActionExecutedContext context)
{
sw.Stop();
foreach (var filterDescriptor in context.ActionDescriptor.FilterDescriptors)
{
if (filterDescriptor.Filter is PerformanceLoggingExpectedMaxAttribute)
{
var expectedMaxAttribute = filterDescriptor.Filter as PerformanceLoggingExpectedMaxAttribute;
if (expectedMaxAttribute != null) ExpectedMax = expectedMaxAttribute.ExpectedMax;
break;
}
}
if (sw.ElapsedMilliseconds >= ExpectedMax)
{
_logger.LogInformation("Test log from PerformanceLoggingActionFilter");
}
}

ASP.NET MVC 4: Only allow one request at a time

In my ASP.NET MVC Application, I want to handle all requests sequentially; no action/controller code should be executed concurrently with another. If two requests come in at similar times, it should run the first one first, then the second one when the first one is done.
Is there a better way of doing this besides using a global lock variable?
EDIT: The application is more of a batch/service over the web that performs web service calls and cleans up a database. Different URLS in the site lead to different batch operations. This is not a site for end-users. Thus, I need to make it so that only one request to a URL (which will do some batch operations) will be done at a time, otherwise the batch operation could be corrupted if code for it runs concurrently with itself, or other batch operations. In fact, if another request comes when one is currently executing, it should not be run at all, even after the previous one finishes; it should just give an error message.
I would like to know if there was a way to do this in IIS instead of code. If I have a global lock variable, it would make the code more complicated, and I might run in a deadlock where the lock variable is set to true but never can be set to false.
EDIT: Sample code of implementation plan
[HttpPost]
public ActionResult Batch1()
{
//Config.Lock is a global static boolean variable
if(Config.Lock) { Response.Write("Error: another batch process is running"); return View(); }
Config.Lock = true;
//Run some batch calls and web services (this code cannot be interleaved with ExecuteBatchCode2() or itself)
ExecuteBatchCode();
Config.Lock = false;
return View();
}
[HttpPost]
public ActionResult Batch2()
{
if(Config.Lock) { Response.Write("Error: another batch process is running"); return View(); }
Config.Lock = true;
//Run some batch calls and web services (this code cannot be interleaved with ExecuteBatchCode1() or itself)
ExecuteBatchCode2();
Config.Lock = false;
return View();
}
Would I need to be worried about a case where the code does not reach Config.Lock = false, resulting in Config.Lock = true forever, causing no more requests to be served?
You have accept request as much as you can, people don't like waiting in front of browser.
But after, on serve side, yuo can push them into (say) Queue<T> and process them in sequence.
In short:
accept in async way
process, on the server, in sequence
You could write an attribute:
public class ExclusiveActionAttribute : ActionFilterAttribute
{
private static int isExecuting = 0;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (Interlocked.CompareExchange(ref isExecuting, 1, 0) == 0)
{
base.OnActionExecuting(filterContext);
return;
}
filterContext.Result =
new HttpStatusCodeResult(HttpStatusCode.ServiceUnavailable);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
base.OnResultExecuted(filterContext);
Interlocked.Exchange(ref isExecuting, 0);
}
}
then use it on the controllers/methods that you want to control:
[ExclusiveAction] //either here, for every action in the controller
public class MyController : Controller
{
[ExclusiveAction] //or here for specific methods
public ActionResult DoTheThing()
{
//foo
return SomeActionResult();
}
}
the above code does not work probably because when request 1 is running and send request 2, app return service unavailable, it's good but if request 1 doesn't completed and again send request 2 to app, app running both request at same time. I'm reviewed code and change it.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ExclusiveActionAttribute : ActionFilterAttribute
{
private static int _isExecuting = 0;
private static int _isDuplicated = 0;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (Interlocked.CompareExchange(ref _isExecuting, 1, 0) == 0)
{
base.OnActionExecuting(filterContext);
return;
}
Interlocked.Exchange(ref _isDuplicated, 1);
filterContext.Result = new StatusCodeResult((int)HttpStatusCode.ServiceUnavailable);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
base.OnResultExecuted(filterContext);
if (_isDuplicated == 1)
{
Interlocked.Exchange(ref _isDuplicated, 0);
return;
}
Interlocked.Exchange(ref _isExecuting, 0);
}

ASP.NET MVC - what is the earliest point in the request cycle where you can detect a static resource request?

To give the question some context, I've written a profiler which is called on Application_BeginRequest but it's logging everything (ie javascripts, images etc). While it would be possible as a last resort to add filtering to the profiler client, I'd much rather only activate the profiler when it can be determined that the request requires routing. Ideally it would be in Application_BeginRequest but I don't think it would be possible without redundant processing of the incoming request for routing...
So in short, when is the earliest point in the request life cycle that I can determine if a request is for a static resource or not, and how would you go about it?
Is it perhaps possible to derive from or hook into System.Web.Routing.RouteTable and call my profiler code from there?
There are various options.
First - to determine the static file using Request.PhysicalPath - check out:
Check for a static file during Application_BeginRequest?
One alternative would be to have this as a handler and use the path to note the file types to include (*.aspx) for example in your web.config. Then you can have access to events pretty early on (see asp.net pipeline)
Or just use an httpmodule - check everything and only profile the non-physical items as you mention.
Or - use your current method with the first link to simply check Request.PhysicalPath and hope that works for you : )
I would rather use MVC Filters for profiling since MVC Filters allow to add pre- and post-processing behaviours and filterContext parameter should give you enough information.
For example, I would create ProfilerAttribute for profiling
public class ProfilerAttribute : FilterAttribute, IActionFilter, IResultFilter, IExceptionFilter {
public void OnActionExecuting(ActionExecutingContext filterContext) {
Debug.WriteLine("Before Action is executing");
}
public void OnActionExecuted(ActionExecutedContext filterContext) {
Debug.WriteLine("After Action is executed");
}
public void OnResultExecuted(ResultExecutedContext filterContext) {
Debug.WriteLine("After Action Result is executed");
}
public void OnResultExecuting(ResultExecutingContext filterContext) {
Debug.WriteLine("Before Action Result is executing");
}
public void OnException(ExceptionContext filterContext) {
Debug.WriteLine("oops! exception");
}
}
and register as GlobalFilter in Global.ascx....
public static void RegisterGlobalFilters(GlobalFilterCollection filters) {
filters.Add(new HandleErrorAttribute());
filters.Add(new ProfilerAttribute());
}
Hope it can help. Thanks.
update: Forgot to mention that the MVC Filter only executed after the routing is matched. So, you don't need to filter out for static resources since it was already done by MVC.
I've tried approaching it from a different angle and am much happier with the result. Basically - why detect static resource requests when you can just not 'route' them at all? In global.asax:
private readonly string[] STATIC_CONTENT_PATHS = new string[] { "css", "js", "img" };
public static void RegisterRoutes(RouteCollection routes)
{
foreach (string path in STATIC_CONTENT_PATHS) { routes.IgnoreRoute(path + #"/{*foo}"); }
// other MapRoute calls here
}
Now I no longer need to check for static requests, although if I do want to for whatever reason I can still do the following:
protected void Application_BeginRequest()
{
if (IsStaticRequest())
{
// do something here
}
}
private bool IsStaticRequest()
{
var result = Request.Url.AbsolutePath.Split('/');
if (result.Length < 2) return false;
return STATIC_CONTENT_PATHS.Contains(result[1]);
}
Since I know with absolute certainly what the only paths I'll be serving static content from are, this seems like a fairly robust solution. I'm still open to other ideas but I think this suits my needs well.

Categories