Monday, September 18, 2006

Lightweight Promotable Transactions and Connection Pooling

We're moving an existing system from a SQL 2000 backend to SQL 2005. The system uses a custom persistence framework and System.Transactions.TransactionScope for all transactions. The framework was developed with the best practices for connection pooling in mind. Basically, connections are cheap; open them when you need them and close them when you're done. This results in a connection for each type being fetched during a request. For example, a Customer with an Address would require 2 connections, one to fetch the Customer and one to fetch the dependent Address.

When SQL 2005 enters the picture, however, we have a problem. TransactionScope and SQL 2005 are smart enough to realize that not all transactions are distributed, so they start out as local ADO transactions and are supposed to be seamlessly promoted when necessary. There are 2 problems we've run into so far. The first is that it's way too easy to trigger a promotion. Although I guess it makes sense, opening a second connection to the same data source always triggers a promotion. The second problem arises if the first connection is busy (say, reading a Customer object from a DataReader) when the second connection is open. This causes an immediate failure since the first connection is blocked, preventing a promotion.

Although you can work around the blocked connection problem with a new SQL 2005 feature, Multiple Active Result Sets (MARS), it still seems a bit gross. I may end up rewriting bits of the persistence framework to properly share a single connection, queuing the objects to be loaded and then working through them rather than having multiple connections with multiple DataReaders going at once.

Monday, September 04, 2006

HttpListenerContext is not serializable

Today I started working on my own subclass of HttpWorkerRequest so that my webserver could handle POSTs and binary data like rails.png on the Welcome Aboard page. After writing all the code to handle the necessary overrides I went to actually run the thing and discovered the troubling fact in my subject line. HttpListenerContext is not serializable. Since my host runs in a separate AppDomain, this is a big problem.

I thought about trying to make some serializable dummy class to shuttle data back and forth, but then I came across Aaron Skonnard's Service Station article in MSDN Magazine here where he hit the same thing. His solution was just to move the HttpListener into the host's AppDomain, which is exactly the right thing to do. After doing that, everything works great. I successfully served the whole Rails Welcome Aboard page including the png (and the AJAX link for environment info), as well as a few test aspx pages with postbacks. There are still some rough edges (no default page handling, untested auth, etc.), but I think this is good enough to move on to working with a pool of Ruby processes for handling Rails.

Sunday, September 03, 2006

More Http.sys and Rails

Magically I found some time to work on my web server for Rails based on Http.sys. In the interest of having something definitely working, I went down the path of creating a basic CGI server. Given a virtual directory and a physical path to a Rails public folder, I can now successfully serve pages, with a few caveats.

One minor thing is that I've only tested a few pages in my first ever Rails app, so I'm sure there are things I've missed. I haven't attempted to support things like auth or https yet, but those should definitely be possible.

More importantly, I'm using HttpRuntime.ProcessRequest for serving static content, but I'm only using SimpleWorkerRequest at the moment. This means binary files as well as POSTs and things don't work. It looks like I'm going to have to subclass HttpWorkerRequest myself to make things work. I've looked at the Cassini source for how they do it (and to see if I could just use that), so this shouldn't be too bad. Not exactly fun though.

Once I get those things handled I'll move onto a pooled interpreter approach and leave the horrible performance of CGI behind. Seeing the Rails 'Welcome aboard' page served from my code was nice, even if the images didn't work yet. Soon.