Register
Tuesday, March 16, 2010
 
 DBAs And ProgrammersBlog
  
News! Minimize
   
 
 Print   
 
Misc Blog Stuff Minimize
   
 
 Print   
 
The Reluctant DBA Minimize
 
 
 
 Print   
 
Reluctant DBA Minimize
   
 
  
 
Reluctant DBA Minimize
   
 
  
 
The Reluctant DBA Minimize
 
Jun11

Written by:CarpDeus
6/11/2009 7:42 AM 

Windows Azure isn't just about Azure Storage Services, it's also about running hosted programs in the sky. And this is actually a critical part of Azure Storage Services, even though it may not seem like it. The series so far has introduced Azure Storage Services and talked about what you can do with each of the pieces, but the time has come to take it to the next level, which includes taking care of some of the management issues that SQL Server and other RDBMSs do for us.

Now, I have said that one of the reasons that Azure Table Storage scales better than SQL Server is because it is simpler and doesn't have all the overhead of managing the data that SQL Server does. And you might accuse me of reinventing the wheel. But there are certain pieces of data management that make sense. Take what SQL Server does when we ask it to process something like this:

SELECT c.CustomerName, o.OrderDate, o.OrderAmount
FROM Customer c INNER JOIN Order o ON
o.CustomerID = c.CustomerID
WHERE o.OrderDate BETWEEN '2009-06/09' AND '2009-06-10'

SQL Server takes all of the advantages of a Relational Database and pulls out selected bits of information from two tables that match criteria. Since Azure Table Storage (ATS) doesn't support JOINs, reproducing this in AQS, you'd need to first get all of the Order entities for the data range and then either build a filter against the Customer entities or build an internal table representation of the Orders and make individual queries for each of the CustomerIDs to get the CustomerName. Or, every time we add an Order Entity, we also write a summary record out that contains the data that we want to be able to query later. This is really what SQL Server does when you create a Indexed View. The difference being that we'll do it in an out of band way using Azure Queue Storage and Worker Roles. But, before we can do that, we need to understand Worker Roles, which are a part of...

Azure Hosted Services

This is the name given to Microsoft's Cloud Server offering. The simplest way to explain this is that it's a server that Microsoft allows you to run programs on. When you deploy a Hosted Service Package what you're really doing is asking Microsoft to create a new, virtual server running whatever operating system they use (doesn't matter to us) and to install the software we've written on it. They do all this in a matter of a few minutes, which is nothing less than astonishing. When was the last time you set up a server? How long did it take to get it operational? Even if you're using virtual servers, to have the operation down so smoothly that it takes no human intervention to get a new server up, running and with your software installed in minutes is amazing.

Now, there are some caveats to this. Just because it's a fully functional virtual server doesn't mean that MS is giving you access to it. They are taking the role of the IT department and saying, "Here's your server, all set up and running. You can do these things within it but you don't have full access to just RDP into the system and do whatever you want." And I'm ok with that. By limiting the access we have, MS ensures that things work as flawlessly as possible. And there's a lot you can do within your...

Hosted Service Packages

Hosted Service Packages are install packages that the Azure Project Manager runs. They are actually zip files (rename the .cspkg to .zip and you can look inside) that contain files and data that's designed to allow your new virtual server to unzip and install your software.

To write a Hosted Service Package, you're going to need a couple of things. First, you'll need a copy of the Azure SDK (downloadable from the Azure Site here). Next you'll need a computer running Windows Vista, Windows 7 or Windows Server 2008. This is because the Developer Fabric and Developer Storage only runs under IIS 7 which doesn't run on other versions of Windows. I downloaded a VM trial version of Windows Server 2008 to use as my development environment. Finally you'll need VS 2008 or VS 2010 and SQLExpress with either version of Visual Studio.

What do you get when you install the SDK? Microsoft summarizes it nicely:

Windows Azure Tools for Microsoft Visual Studio includes:

  • C# and VB Project Templates for building Cloud Services
  • Tools to change the Service Role configuration
  • Integrated local development via the Development Fabric and Development Storage services
  • Debugging Cloud Service Roles running in the Development Fabric
  • Building and packaging of Cloud Service Packages
  • Browsing to the Azure Services Developer Portal
  • SSL Certificate selection

The key that we're looking for is the first bullet point, new project templates for building Cloud Services. These templates create solutions that contain either a worker role, a web role or both roles along with a Cloud project. And, having said that, it's time to discuss...

 

Web Roles, Worker Roles and Cloud Packages

For your first Cloud Services Project, I'd recommend creating a project that contains both Web Role and Worker Role. That way you'll be able to work with everything in one place for your initial experience. Also, I'm going to tell you now that if you've done any programming, this really isn't anything new for you, it's just got slightly different names.

Web Roles

Web Roles are really nothing more than a web site. If you've built an ASPX web site, then you can build a web role. Same technology applies. You create ASPX pages, HTML pages. use Web Config, just like you normally do. In fact, unless you're implementing something that actually requires part of the Azure technology specifically, you can can even run and debug your code using the localhost instance of IIS if you want to set the Web Role project as your start up project (the Cloud project is the start up project by default). Really, that's all I have to say about web roles at the moment. Almost everything running on CloudQuotes is running without using any of the Azure specific technologies. The only pages that use any of the Azure technology are the two that copy demonstration data and the only reason I do that with those is because I am getting information from the cloud configuration files (see Cloud Packages below) because I wanted to see how that worked.

Really, there's not much more to say about Web Roles. If you don't have a database back end, you could copy your entire web site project into the Web Role project and have it run.

Worker Roles

Worker roles are kind of like simplified Windows Services and they are where we are going to spend the majority of this blog post. The default Worker Role project comes with two methods defined. One, Start(), is called when the Worker Role is first started. The default code for that method does nothing more than sleep 10 seconds and record an entry in the Cloud Log. The other, GetHealthStatus(), is a method that gets called every so often to determine whether the Worker Role is having issues. The default is to return a Healthy status and is used to determine whether to restart the Worker Role.

Many people will tell you that debugging Worker Roles is hard for lots of reasons. I'm here to tell you that it isn't true, you just need to change your opinion of what a Worker Role is.

A Worker Role is nothing but a Wrapper for a DLL that does all of the work.

I'll even go further and say that a best practice of a Worker Role is going to have code that looks like this:

   1:using System;
   2:using System.Collections.Generic;
   3:using System.Threading;
   4:using System.Linq;
   5:using System.Text;
   6:using Microsoft.ServiceHosting.ServiceRuntime;
   7: 
   8:namespace CloudQuotes_WorkerRole
   9:{
  10: public class WorkerRole : RoleEntryPoint
  11: {
  12: 
  13:  RoleStatus currentRoleStatus = RoleStatus.Starting;
  14:  DateTime lastHealthyCheck = DateTime.UtcNow;
  15:int maxSecondsWithoutHealthCheck = 60;
  16: 
  17:public override void Start()
  18:  {
  19:   currentRoleStatus = RoleStatus.Starting;
  20:try
  21:   {
  22:    bulkProcessing bp = new bulkProcessing();
  23:    bp.OnLogHandler(libraryNotification);
  24: 
  25:    RoleManager.WriteToLog("Information", "Starting Worker Process");
  26:    currentRoleStatus = RoleStatus.Healthy;
  27:while (true)
  28:    {
  29:try
  30:     {
  31:     lastHealthyCheck = DateTime.UtcNow;
  32:      bp.Process();
  33:      Thread.Sleep(1000);
  34:     }
  35:catch (Exception ex)
  36:     {
  37:string errorID = Guid.NewGuid().ToString();
  38:      RoleManager.WriteToLog("Error", string.Format("Error logged in processing, see Error {0}", errorID));
  39:      RoleManager.WriteToLog("Verbose", string.Format("Error {0}\r\nDetails:{1}", errorID, ex.ToString()));
  40:      currentRoleStatus = RoleStatus.Unhealthy;
  41:     }
  42:    }
  43:   }
  44:catch (Exception ex)
  45:   {
  46:string errorID = Guid.NewGuid().ToString();
  47:    RoleManager.WriteToLog("Critical", string.Format("Cannot start worker role, see Error {0}", errorID));
  48:    RoleManager.WriteToLog("Verbose", string.Format("Error {0}\r\nDetails:{1}", errorID, ex.ToString()));
  49:    currentRoleStatus = RoleStatus.NonExistent;
  50:   }
  51:  }
  52: 
  53:public override RoleStatus GetHealthStatus()
  54:  {
  55:if (lastHealthyCheck.AddSeconds(maxSecondsWithoutHealthCheck ) < DateTime.Now)
  56:    currentRoleStatus = RoleStatus.Unhealthy;
  57:return currentRoleStatus;
  58:  }
  59: 
  60:public void libraryNotification(string messageType, string Notification)
  61:  {
  62:  lastHealthyCheck = DateTime.UtcNow;
  63:   RoleManager.WriteToLog(messageType, Notification);
  64:  }
  65: }
  66:}

The Worker Role defined in this code really only does two things. It instantiates an object in a compiled DLL and executes a single function against it every 10 seconds and it logs any data to the Logs that the compiled DLL passes it.. Any time I need to change this code, I can fully test and debug my library like I would a service and then open up my Worker Role project, link my updated library and post it to the cloud. But there are some other pieces in this code that relate to having a good Worker Role and let's discuss them in depth.

First we have three global variables: currentRoleStatus, lastHealthyCheck and maxSecondsWithoutHealthCheck. These all deal with helping the cloud to know what state our Worker Role is in. The RoleStatus (Healthy, NonExistent, Started, Starting, Stopped, Stopping, Unhealthy) is a mechanism that the Cloud App can use to determine whether it needs to do anything with the Worker Role. If the Worker Role's status is Unhealthy, the role will be restarted. The other two variables are used to determine if the Role might be stuck. Whenever the process kicks off or the called library sends notifications back to the Worker Role, we update the lastHealthyCheck value to be the current time. Then, when the Cloud App calls GetHealthStatus to determine whether the Worker Role needs help, we check to see if the lastHealthyCheck happened within the timeframe that we have allotted. If it hasn't, we consider the Role to be stuck and tell the Cloud App that the role isn't healthy and it restarts it.

And note, as of the current version, while the Worker Role is checked to see what the health status is, the service is not restarted if it is in a status other than Healthy. But the stated goal is that a Role with a status of Unhealthy will be restarted. But this code is forward looking and I stand by the idea of it.

The next section, lines 20-26, are a Try-Catch block that attempts to instantiate the class that does all of the real work. Any failure here means we have a serious problem so, as you can see in the Catch block (44-50), I mark the currentRoleStatus as NonExistent. Which is true, if I can't instantiate the class that does the work, the role really doesn't exist. Then I write an error to the Critical error log with a pointer to the Verbose version of the error and finally I write to the Verbose error log. And the worker role should be done.

If I was able to successfully instantiate the Worker Role, then I start a new Try-Catch block, this one to handle the regular processing of the class (29-34). I assume that the library that's doing all of the real work is utilizing the callback to log any errors so this should only catch errors that aren't being handled in the class, so all I do is log those errors and go on. The other thing to note is that I update lastHealthyCheck before I call the class that does the work.

The final part of the Worker Class is libraryNotification. I assume that any class written to work with a Worker Class will implement a callback handler to be able to pass messages to the Worker Class. Since logging is really the only piece of code that requires the Azure Services Runtime class, we let the worker class handle all of the logging by passing information to it to log and the class that is handling all of the heavy work can be written, tested and debugged without working about the development fabric.

And there you have all you need to know about the Worker Class. Keep it a light-weight wrapper and your life will be much easier. We'll dig into what it's calling next week, but for now let's look at...

Cloud Packages

The last part of the Azure Hosted Services is the Cloud Package. This is a project which contains all of the details necessary for packaging up your Worker and/or Web Roles and posting them to your hosted services account. This project consists of three files: the CSProj file, a CSDEF and a CSCFG file, the latter two being the ones that most interest us.

The most common way of storing information for a Web Application is to put it in the web.config. This can be a bit clunky and a real chore to edit without stopping and starting your web server. It's even harder if you don't have access to your web server, which we don't since it's a virtual machine and, more importantly, we might have multiple machines out there and editing the web config might only change one of them. Instead we have the Cloud Service configuration, which is a collection of Name Value Pairs (NVPs) editable from the page where you manage your projects.

The first step to having something that we can access from either the Worker or Web Role is to define what NVPs we want. These are all found in the CSDEF file, which looks something like this:

xml version="1.0" encoding="utf-8"?>  <ServiceDefinition name="CloudQuotes" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition"> <WebRole name="WebRole"> <InputEndpoints> <InputEndpoint name="HttpIn" protocol="http" port="80" />      </InputEndpoints> <ConfigurationSettings> <Setting name="AccountName" /> <Setting name="AccountSharedKey" /> <Setting name="BulkStorageLocation" />      </ConfigurationSettings>    </WebRole> <WorkerRole name="WorkerRole"> <ConfigurationSettings> <Setting name="AccountName" /> <Setting name="AccountSharedKey" /> <Setting name="BulkStorageLocation" />     </ConfigurationSettings>    </WorkerRole> </ServiceDefinition>

The only parts we can edit are the ConfigurationSetting blocks. You'll notice that there is one for each of the roles. Although they look the same, they don't have to be. In fact, we're going to add an item in the Worker Role to define the maxSecondsWithoutHealthCheck. Now our WorkerRole block in our CSDEF will look like this:

<WorkerRole name="WorkerRole"><ConfigurationSettings><Setting name="AccountName" /><Setting name="AccountSharedKey" /><Setting name="BulkStorageLocation" /><Setting name="maxSecondsWithoutHealthCheck" />
</ConfigurationSettings></WorkerRole>

Now that  we have defined what can be in our Cloud Services config file, let's look at that file:

xml version="1.0"?>  <ServiceConfiguration serviceName="CloudQuotes" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration"> <Role name="WebRole"> <Instances count="1"/> <ConfigurationSettings> <Setting name="AccountName" value="finseldemos" /> <Setting name="AccountSharedKey" value="mysecretkey" /> <Setting name="BulkStorageLocation" value="bulkprocessor2"/>       </ConfigurationSettings>    </Role>   <Role name="WorkerRole"> <Instances count="1"/> <ConfigurationSettings> <Setting name="AccountName" value="finseldemos" /> <Setting name="AccountSharedKey" value="mysecretkey" /> <Setting name="BulkStorageLocation" value="bulkprocessor2"/>       </ConfigurationSettings>    </Role> </ServiceConfiguration>

This is the  config file. As you can see, it's simply name value pairs as attributes in the XML Block. You can add items to the Definition that aren't inlcuded in the config without a problem. But we're going to modify our worker role to have a value for maxSecondsWithoutHealthCheck:

<Role name="WorkerRole"><Instances count="1"/><ConfigurationSettings><Setting name="AccountName" value="finseldemos" /><Setting name="AccountSharedKey" value="mysecretkey" /><Setting name="BulkStorageLocation" value="bulkprocessor2"/><Setting name="maxSecondsWithoutHealthCheck" value="60" />ConfigurationSettings>Role>

The last thing we need to do is modify our Worker Role code to get this value from the config:

int maxSecondsWithoutHealthCheck = Convert.ToInt16(RoleManager.GetConfigurationSetting("maxSecondsWithoutHealthCheck"));

Now, whenever we want to change how long we think our process should run, we just need to use our Azure Project Management (https://lx.azure.microsoft.com/Cloud/Provisioning/Default.aspx)  page to edit the config.

I should also point out that this is where we tell MS how many instances of our worker roles and our web roles we want to have running. We can change the Count value of Instances to reflect the number of instances we want to have running. It must be at least 1 and, during the CTP, it can be no more than 2. Once MS announces and starts charging for this (later this year, after PDC) then we'll probably be able to increase the number of instances we're running but the more instances, the more it will cost.

Publishing Your Project

To publish your project all you need to do is right-click on your Cloud Project and select Publish. This will create a Hosted Service Package. Now go to your Cloud Project Management page (https://lx.azure.microsoft.com/Cloud/Provisioning/Default.aspx)  and select your project. This will show you a screen that looks like this:

AzureHostedServiceManagement1

Click on the Deploy button and you'll be presented with a screen that asks you for three inputs, your CSPKG file, your CSCFG file and a label for the deployment. Once you've filled those in, click the Deploy button.

AzureHostedServiceManagement2

Most deployments are very small. As you can see from the image below, Cloudquotes is only a couple hundred KB. So I don't normally see this screen, but I was doing testing on a slow connection and had time to capture it. It's the same terms and conditions that you had to accept when you signed up.

AzureHostedServiceTermsAndConditions

Now that we have our project published to the web, we need to turn it on and make sure it runs. We do that by pressing the Run button beneath our Staging project. You'll notice that our staging project has a URL that uses a UUID for the subdomain. That is it's temporary home where we can test it and make sure everything works.

AzureHostedServiceManagement3

Once you're Staging deployment is running, you can click on the Web Site URL and go to your staging site. You can tell when it's running because it will show Started, as it does for the Production service.

Once you've thoroughly tested it, then click on the button with arrows between the Production and Staging projects.

AzureHostedServiceManagement4

This will change the DNS Entries to swap the two virtual Servers. And suddenly everyone is talking to your new version.

AzureHostedServiceManagement5

Best Practice Tip would be to Suspend but don't delete the Staging project once the two are swapped. That's because MS will charge you for processing time on both computers, once they start charging for the service. If, however, someone encounters a bug that's bad enough, you can swap the older, more stable in in a few minutes.

Wrapping up

That's the quick overview of Azure Hosted Services. If you've done any programming of ASPX pages, then you're ready to program Web Roles. I've covered Worker Roles in more depth and discussed publishing your application.

Next week I'll start out talking about creating a DLL that you can call from your Worker Role and then dig into some of the fun and exciting things that will make Azure Storage more useful.

All Posts in Series:

 

Copyright ©2009 Carp Deus

Tags:

Your name:
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Title:
Comment:
Add Comment  Cancel 
 
 
  
 
Privacy Statement | Terms Of Use Copyright 2001-2008 by ReluctantDBA.com