Thread starvation in MVC

 

On IIS, the .NET Framework maintains a pool of threads that are used to service ASP.NET requests. When a request arrives, a thread from the pool is dispatched to process that request. If the request is processed synchronously, the thread that processes the request is blocked while the request is being processed, and that thread cannot service another request.

This is generally not a problem insofar as ASP.net may have enough threads (depending on version) to accommodate a few blocked threads, however when all threads are blocked you’ll see a 503 http error presented – now in thread starvation.

Firstly, lets talk about standard asp.NET, how can we overcome this problem?

Seasoned .NET developers may attempt to begin invoke on a delegate (using lambdas these days Hot smile) or use the ThreadPool.QueueUserWorkItem, however both these approaches are futile as the both draw from the same thread pool as asp.NET Confused smile

Another naïve approach would be to serve up your own thread,  think about this for a second, your website is there to handle multiple requests…! Needless to say it could promptly run out of resources as a new thread would be created for each request.

The asp.NET solution is to use async pages , <%@ Page Async=”true” …>  / Page.AddOnPreRenderCompleteAsync 

So what about mvc?

MVC – Async Actions

clip_image001

 

  1. The Web server gets a thread from the thread pool (the worker thread) and schedules it to handle an incoming request. This worker thread initiates an asynchronous operation.
  1. The worker thread is returned to the thread pool to service another Web request.
  1. When the asynchronous operation is complete, it notifies ASP.NET.
  2. The Web server gets a worker thread from the thread pool (which might be a different thread from the thread that started the asynchronous operation) to process the remainder of the request, including rendering the response.

 

SYNC

public class PortalController: Controller {
   
public ActionResult News(string
city) {
        NewsService newsService =
new
NewsService();
        ViewStringModel headlines = newsService.GetHeadlines(city);
       
return
View(headlines);
    }
}

ASYNC

public class PortalController : AsyncController {
   
public void NewsAsync(string
city) {

AsyncManager.OutstandingOperations.Increment();
        NewsService newsService =
new
NewsService();
        newsService.GetHeadlinesCompleted += (sender, e) =>
        {
            AsyncManager.Parameters[
"headlines"
] = e.Value;
            AsyncManager.OutstandingOperations.Decrement();
        };
        newsService.GetHeadlinesAsync(city);
    }

public ActionResult NewsCompleted(string[] headlines) {
       
return View("News", new
ViewStringModel
        {
            NewsHeadlines = headlines
        });
    }
}

Note:

  1. New derivation
  2. Matching Async/Completed calls

The future is live

 

Probably like a lot of other computer people I find myself working with more than one computer. There in lies the problem, is it worth the overhead of manually having to synchronize all these pcs?

Sure source code is and always has been handled by a source control repository, tfs/svn etc.

But what about documents; during the week I started setting up a new desktop machine, tonight while working on a java project (i know i know) i found that i needed to copy a few strings into the clipboard as I was developing, now I usually do this from within vs2010 but I’m not sure how to do this from the Java Netbeans IDE so i though I’d use MS Onenote.. there in lied the problem. I wanted to access the notebook I had on my laptop but I didn’t want to start an import/export process (so 90ies Rolling on the floor laughing).

Turns out that onenote 2010 allows me to save my notebook on a network share (not so exciting because i could do this with UNC paths); but moreover it allows you to keep your notebook synchronized with windows live office space.

So now I‘ve got my notebooks sitting in the cloud and cached and accessed locally on different computers. Now I’ve not really invested much if any time in could services, but I think it’s time to start, I’ve a sneaky suspicion that going forward operating systems will become more and more cloud integrated. I can’t see anyone choosing to manually upload and download documents to servers when this can now easily be managed by hitting that save button.

Not just a new look for 2011

Have updated a host of new features on my blog, (running Blog Engine 2.0 released on 1st January)

I’m now writing my blogs offline with Wndows Live Writer, and click of the mouse posting them to www.briankeating.net, no more manually fighting with rich text markup formatting  and html.

New features:

  • Upgraded to .NET 3.5
  • New Control Panel - complete makeover
  • Recaptcha, captcha to reduce spam for Comments and Contact Form
  • SimpleCaptcha, captcha to reduce spam
  • TypePadFilter, anti-spam comment filter
  • Disqus, optionally replace the normal comment system with Disqus
  • SQL CE 4.0 support - standalone DB storage in the App_Data directory
  • Recycle bin for Posts, Pages and Comments
  • New Dashboard in control panel
  • Posts list and Pages list - grid of all Posts and Pages in one easy to see area
  • Added jQuery, automatically included and available in pages
  • Security improvements - related to external content the blog may download
  • New Media Elements JS library extension for HTML5 video support
  • Themeable Widget Containers & Newsletter widget Notification Emails
  • New Syntax Highlighter extension - most popular syntax highlighting library
  • Roles & Rights system - create Roles and assign Rights to each role
  • Private blog feature - require users to be logged in to access the blog
  • Option to allow self-registration for visitors
  • Support for Unicode characters in the URL for Posts and Pages.
  • Improved JS/CSS minification speed and size
  • Latest version of tinyMCE WYSIWYG editor

Hello IIS 7.5 Express

Cassini; We’ve all used it and loved it, now it’s the end of the road for the visual studio webserver.

It gets replaced by  IIS 7.5 Express

  • You can install it on its own. It's IIS, except it runs as a user process rather than a service. Cassini (Visual Developer Web Server) is dead! It's "just in time" IIS. There when you need it, and not running when it's not used.
  • This is the web server that Web Matrix uses today, but it'll be enabled in Visual Studio 2010 when SP1 comes out.

MVC3 Released

Today Microsoft announced the (actual, final, honest) releases of:

  • ASP.NET MVC3 with Razor
    • Lots of new features, the new Razor syntax, more extensibility hooks, new JavaScript features, better validation, easier caching, better dynamic support, and lots more, check it out Hot smile

Open Redirection Attack prevention

If you've had a look at the MVC3 preview generated code you will notice the  Url.IsLocalUrl method getting called from the LogOn action.

[code:c#]

[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
    if (ModelState.IsValid)
    {
        if (MembershipService.ValidateUser(model.UserName, model.Password))
        {
            FormsService.SignIn(model.UserName, model.RememberMe);
            if (Url.IsLocalUrl(returnUrl))
            {
                return Redirect(returnUrl);
            }
            else
            {
                return RedirectToAction("Index", "Home");
            }
        }
        else
        {
            ModelState.AddModelError("",
            "The user name or password provided is incorrect.");
        }
    }
 
    // If we got this far, something failed, redisplay form
    return View(model);
}

[/code]

This prevents philishing attacks by ensuring that after a successful log on the user only gets directed to the local site and not another site.

Take for example I send you a email with a link to /Account/LogOn?returnUrl=http://brainkeating.net/Account/LogOn
After you successfully enter your log on detail @ briankeating.net you would get redirected to www.brAInkeating.net if you are not quick enough to notice you could now be presented with an identical logon page at this different url, oh darn i typed my password wrong...., BrAIn Keating is a nasty piece of work, he will log your login details to his database and then use it to access briankeating.net with our credentials, not after you login @ brAInkeating.net you will get redirected back here to briankeating.net (where you've already authenticated and be none the wiser).

So what if you are still on MVC2 (most of my web apps these days are...) what can you do to avoid this attack...

That;s an easy one to answer, I've been a lazy bugger and lifted this code from www.asp.net

[code:c#]

public bool IsLocalUrl(string url) {
    return System.Web.WebPages.RequestExtensions.IsUrlLocalToHost(
        RequestContext.HttpContext.Request, url);
}

[/code]

The IsUrlLocalToHost method contains the actual validation logic below

[code:c#]

public static bool IsUrlLocalToHost(this HttpRequestBase request, string url) {
    if (url.IsEmpty()) {
        return false;
    }
 
    Uri absoluteUri;
    if (Uri.TryCreate(url, UriKind.Absolute, out absoluteUri)) {
        return String.Equals(request.Url.Host, absoluteUri.Host,
            StringComparison.OrdinalIgnoreCase);
    }
    else {
        bool isLocal = !url.StartsWith("http:", StringComparison.OrdinalIgnoreCase)
            && !url.StartsWith("https:", StringComparison.OrdinalIgnoreCase)
            && Uri.IsWellFormedUriString(url, UriKind.Relative);
        return isLocal;
    }
}

[/code]

 

Oracle Database Restore

How to create an oracle user/schema and resore an existing database to this new schema.

 

drop tablespace xxx_demo_data including contents and datafiles;


CREATE TABLESPACE xxx_demo_data
       DATAFILE 'd:\oradata\abci\xxx_demo.DBF '
       SIZE 10M
       AUTOEXTEND ON NEXT 10M
       MAXSIZE UNLIMITED;

drop user xxx_demo cascade;
 
   CREATE USER xxx_demo
       IDENTIFIED BY xxx_demo
       DEFAULT TABLESPACE xxx_demo_data
       TEMPORARY TABLESPACE temp;


GRANT CREATE SESSION TO xxx_demo;  

GRANT CREATE TABLE TO xxx_demo;
GRANT CREATE VIEW TO xxx_demo;
GRANT CREATE SEQUENCE TO xxx_demo;
GRANT CREATE TRIGGER TO xxx_demo;
GRANT RESOURCE TO xxx_demo;

ALTER USER xxx_demo QUOTA unlimited ON xxx_demo_data;

// change the passworkd.
//drop to command line

impdp xxx_demo/{passwd} DIRECTORY=expdp_dir DUMPFILE=20101220-DEMO.DMP REMAP_SCHEMA=xxx:xxx_demo  REMAP_tablespace=xxx:xxx_demo

Asyncronous WF4 activities

Anyone that's ever written UX/GUI code will know that while it once was acceptable (unavoidable) to block the UX thread with long running operations, it's now almost always done in a background thread/task etc.

Well with WF4 it's pretty much the same story, it's not the best idea to block the workflow thread while preforming longing running operations.

Take the following example, where I make a webservice call to a server for some information, webservices can take quite some time depending on network load latency etc.

public sealed class GetCurveTenor : AsyncCodeActivity

    {

        public InArgument<string> CurveUri { get; set; }

        public OutArgument<Dictionary<DateTime, double>> CurveDetail { get; set; }

 

        protected override IAsyncResult BeginExecute(

            AsyncCodeActivityContext context, AsyncCallback callback,

            object state)

        {

            Func<string, Dictionary<DateTime, double>> asyncWork =

                curveUri => RetrieveCurveDetail(curveUri);

            context.UserState = asyncWork;

            return asyncWork.BeginInvoke(

                CurveUri.Get(context), callback, state);

        }

        protected override void EndExecute(

            AsyncCodeActivityContext context, IAsyncResult result)

        {

            Dictionary<DateTime, double> curveDetail =

                ((Func<Int32, Dictionary<DateTime, double>>)

                    context.UserState).EndInvoke(result);

            if (curveDetail != null)

            {

                CurveDetail.Set(context, curveDetail);

            }

        }

 

        private Dictionary<DateTime, double> RetrieveCurveDetail(string curveUri)

        {

            Dictionary<DateTime, double> result = new Dictionary<DateTime, double>();

            // Do server stuff ...

            return result;

        }

    }

Most of the code above should be self explanatory.
If you've not seen the "Func" syntax before it's basically just a delegate defined like this (in .NET 4)

public delegate TResult Func<in T, out TResult>(T arg);  (Note the .NET 4 in modifier to indicate contravariance)

I've used lambdas to point to delegate towards the RetrieveCurveDetails function, it's this function that gets executed on the background thread.

Hope this has been of some assistance.

 

 

 

ReHosting the workflow designer

If you've attempted to host the workflow designer in .net <= 3.5 you'll remember it's not such an easy achievement.

However doing so in wpf .net 4.0 is quite trivial.

Have a look at the Visual Studio 2010 .NET 4 training kit and you'll see for your self.

Here's my first appempt this evening that took all of 10 minutes.

 Make sure to download the training kit even if you are an experienced developer, it's got some gems in it.