Migrating AnandTech from Macromedia ColdFusion to ASP.NET
Microsoft Visual Studio .NET
Microsoft .NET Framework
Summary: AnandTech is a consumer information technology site that delivers extensive coverage of the latest computer hardware and IT news. The site started as a simple, static HTML site, authored in FrontPage back in 1997. As web technology advanced, the first dynamic version of the website was created using ColdFusion 4.0 on a Sun Sparc machine. Over the years, AnandTech migrated to Microsoft Windows 2000 Server, with ColdFusion MX serving up its dynamic pages. (7 printed pages)
The Migration Mandate
The Migration Mandate
AnandTech's mandate to migrate to Microsoft ASP.NET was based on two main reasons: First, we wanted to reduce our TCO (total cost of ownership). For every server added to our web cluster, an additional $1299 had to be spent for ColdFusion. Conversely, ASP.NET is free on the Windows platform. The other reason for migrating was stability. AnandTech serves approximately 50 million pages per month, and there are anywhere from 3,000 to over 16,000 simultaneous users on the site depending on the time of day and content served. Since AnandTech has such a vast fluctuation in traffic, it was important that the infrastructure be very scalable.
We had a few issues over the past year with ColdFusion on the main site and on our discussion forums, and as a result our up-time was suffering. At this point, a decision had to be made: either deploy ColdFusion on a more robust enterprise J2EE Server, or rewrite the main site in ASP.NET. Purchasing a few licenses of IBM WebSphere would cost us tens of thousands of dollars, and then we'd have to purchase additional ColdFusion J2EE edition licenses, which cost a few thousand per CPU. Obviously, from a cost standpoint, ASP.NET made a lot of sense for our infrastructure, since we had the engineering expertise in-house to rewrite the code in ASP.NET.
Our main concern when we started the migration was the time factor. How long was it going to take? You can have all the planning meetings that you want, but this, our first migration to a more structured language, was a guess at best. The success of the site migration was largely due to the performance benefits that we realized and the flexibility of the development environment. We used Microsoft Visual Studio .NET 2003 for all of the sites' development. The code hinting and reflection technology in Visual Studio made the migration process much easier, saving us an incredible amount of time.
The migration to ASP.NET took approximately two months, a month for development of the main site and another month for QA and performance testing. We had to rewrite two main applications. The first was the main site itself, which serves up our editorial content using a home-grown CMS solution. Since AnandTech's revenue model is based on internet advertising, we also had to migrate our Ad delivery software. To track and deliver our advertising, we used a ColdFusion-based Ad Management software called "FuseAds." With some help from the vendor, we migrated that software to the ASP.NET platform in short order.
Writing the API
The ColdFusion version of our site used CFCs to encapsulate business logic. We used method calls throughout our application to perform various operations, like getting an article or posting a comment to an article. To accomplish the same structure in Microsoft Visual Basic .NET, we created a class called AnandTechAPI and wrote our methods within that class. To instantiate the class, we created an Init class, which handles initializing the API and any other startup functions required when a web page is requested. This class is then inherited by the aspx pages throughout the site.
For example, the default.aspx file has the following at the top of the page:
<%@ Page Language="vb" AutoEventWireup="false" inherits="anandtech.init" %>
This tells the page to inherit our init class. In our init class, we use the Page_Load event to instantiate our API. The constructer for the API looks something like this:
atObj = New AnandTechAPI().
The main advantage that ColdFusion has over other web languages is its RAD approach to development. Common tasks are obfuscated into "tags," which simplify the development process by handling the tedious syntax work for the developer. To make our transition easier, we wrote a few functions that replicated functions commonly used in our ColdFusion version of the site.
In ColdFusion, these functions take a date/time value and format it into user specified date and time formats. We used this function frequently, so this was one of the first functions that we migrated. The .NET framework does have its own functions for date formatting, but it's a bit more tedious than DateFormat(date,mask).
<cfoutput>#DateFormat(Now(),"mmmm d, yyyy")#</cfoutput>
This would produce something like "December 11, 2004."
Visual Basic .NET DateFormat function
This function takes a DateTime value and a date mask formatter, "MM/dd/yyyy," for example. The date mask parameter is very similar to ColdFusion's date masks. Valid .NET date format strings can be found on MSDN. Since we only deliver our site in English, our locale was hard coded to "en-US"; valid locales can be found on MSDN.
Public Function DateFormat(ByVal datetoformat As DateTime, _ ByVal datemask As String) As String Dim ReturnDate As String Dim ci As CultureInfo = New CultureInfo("en-US") ReturnDate = datetoformat.ToString(datemask, ci) Return ReturnDate End Function
In Visual Basic .NET, the Replace method of the StringBuilder class is case sensitive by default. In ColdFusion, there are two separate functions: Replace and ReplaceNoCase, where ReplaceNoCase is case-insensitive. Fortunately for Visual Basic .NET, there are some additional parameters that you can pass to it, which make it case-insensitive. We wrapped this in a function to make the migration easier and to make the code more readable.
This would produce text.
Visual Basic .NET ReplaceNoCase function
This function takes the source string, a string for which to search, and a string with which to replace the occurrences found. We omitted the scope parameter that ColdFusion uses, since we always wanted every occurrence replaced.
Public Function ReplaceNoCase(ByVal Text As String, _ ByVal str1 As String, ByVal str2 As String) As String Dim retStr As String = _ Replace(Text, str1, str2, 1, -1, CompareMethod.Text) Return retStr End Function
The purpose of this tag is to convert potentially harmful characters to their HTML equivalents. In ColdFusion, this tag replaces the following characters: <>&", with their HTML equivalents: <>&". Since we used this throughout our code, we wrote a function to perform that function.
#HtmlEditFormat("<b>")# would output "<b>".
Visual Basic .NET version
Public Function HtmlEditFormat(ByVal Str As String) As String Str = Str.Replace("&", "&") Str = Str.Replace(">", ">") Str = Str.Replace("<", "<") Str = Str.Replace(Chr(34), """) Return Str End Function
Good to Know
Migrating to a new platform brings with it the need to learn new syntax, and new ways to perform common tasks. As we progressed with our migration, we spent time searching within documentation and .NET sites looking for equivalent code, and solutions to a few "gotchas" that we ran into along the way.
ColdFusion has several functions that handle the manipulation of string based lists. Since most structured languages don't deal with strings in this manner, we took the more structured approach and used arrays.
<cfset myList = "bob,sue,mary,fred"> <cfoutput>#ListGetAt(myList,"2")#</cfoutput>
This would produce sue.
Visual Basic .NET
Dim myList as string = "bob,sue,mary,fred" Dim myArr() as string = myList.Split(",") Response.write(myArr(1)) 'Note that we asked for element 1, in VB.NET arrays start at 0.
When you query a database and get your records back in a data set, any field that is NULL is returned as an Object of type DBNull. This presents a problem for developers who are used to the way that ColdFusion deals with database nulls. Let's say that you have something like the following in your ColdFusion code:
<cfif Len(myData.myFieldName) gt 0> Do Something </cfif>
In ColdFusion, if myFieldName is NULL, it has a length of 0. If you tried a similar conditional statement in ASP.NET, you'd get an error like "Cast from type 'DBNull' to type 'String' is not valid." We got around this by writing our own function to check the length of a database field.
If DbLen(myDataSet.Tables(0).rows(0).item("myfield")) > 0 then Do Something End if Public Function DbLen(ByVal Field As Object) As Integer Dim len As Integer Dim NewVar As String = "" Dim ObjType As Type = Field.GetType() If ObjType.FullName = "System.DBNull" Then NewVar = Field.Value.ToString Else NewVar = Field End If Return NewVar.Length End Function
Since the site went live with the new ASP.NET framework, we've been constantly monitoring the servers and tracking statistics, and to say that we've been impressed is an understatement. We've had near 99 percent up-time and have seen dramatic increases in performance and scalability of the site infrastructure. Each of our clustered servers use an average of 12 percent CPU, and each server has at least 450 users on them at any given time. Pages execute in an average of 15ms, which is 60-80ms better than what we used to do.
To track our statistics, we wrote a performance counter monitor that resides on each server. It essentially writes the output of various performance counters to an aspx page, which we then feed into a database and run reports, view trends, and monitor new versions of the site code as they are put into play.
A couple of months after our .NET launch, we performed a test on scalability, to see how scalable one server was in the cluster. We monitored a server in the cluster for a period of 10 minutes while all five servers were in the web cluster. Next, we shut off four of the servers, leaving one to serve the load for the website. Below is a table of the before and after performances of this test—impressive results, in our opinion.
Table 1. ASP.NET Server scalability test
|Performance Counter|| Before
(5 servers in the cluster)
(1 server in the cluster)
|Requests per second||12||80|
|Average request time (ms)||15||18|
As you can see, ASP.NET is very efficient. CPU usage climbed to 30 percent, not much considering that nearly 2000 more users were on the server. Request per second were seven times higher, and our average (page) request times were approximately the same, showing the efficiency of the ASP.NET runtime and the scalability of the website itself.
With any migration, there is going to be a learning curve—we're still learning. If you choose to migrate to ASP.NET, you'll find that the developer community is vast and very helpful should you lose your way. Take your time, plan out your migration, and get a copy of Visual Studio .NET. It will save you time and increase your productivity tremendously.
About the author
Jason Clark is the Senior Software & Network Engineer with AnandTech Inc. Jason's role at AnandTech is to administer the server network and to architect and develop the back-end technology that runs AnandTech. Aside from working with AnandTech, Jason is also the Chief Technology Officer of FuseTalk, Inc.