Tuesday, October 6, 2009

A walk in the clouds with SimpleDB

For starters I’m a cloud skeptic, I think it’s too new and untested to make any real long term predictions about where it’s going to go, but having said that it looks very interesting, and if you don’t have the hardware it can be a very inexpensive way to have a very scalable application.

Signing up with Amazon

For starters go to http://aws.amazon.com/simpledb/ and click on the signup and create an account which requires a credit card, just so you know.

As far as the cost, it really is very reasonable to free, based on your monthly usage with the first 25 Machine Hours free and each additional hour costing $0.140, your first GB of storage is free with each additional GB costing $0.25 per GB Per month. For bandwidth your 1st GB of data upload is free with each additional GB costing $0.10; your first GB of download is free with the following price structure for additional download badwith

  • $0.170 per GB - first 10 TB / month data transfer out
  • $0.130 per GB - next 40 TB / month data transfer out
  • $0.110 per GB - next 100 TB / month data transfer out
  • $0.100 per GB - data transfer out / month over 150 TB

While setting up the account was easy, there site is confusing, hard to navigate, and frustrating, my biggest complaint is I have to resign in constantly.

Getting the Amazon Tools

Finding the C# Library for Amazon SimpleDB is a pain, so I’m kindly providing you a link, now this is just source code, which is kind of irritating, why can’t they just give me the DLLs, but on the other side I can look at what they are doing, and how they do it.

Using the Amazon Tools

The first thing to do is build a proxy for interfacing with the simpleDB web service, something like this

   1: public class Proxy
   2: {
   3:     private string PublicKey = String.Empty;
   4:     private string SecretKey = String.Empty;
   5:     public AmazonSimpleDBClient Service{ get; set;}
   6:     private List<string> domains;
   7:     public List<string> Domains
   8:     {
   9:         get
  10:         {
  11:             if (domains==null)
  12:             {
  13:                 domains = GetDomains();
  14:             }
  15:             return domains;
  16:         }
  17:     }
  18:  
  19:     public Proxy()
  20:     {
  21:         PublicKey = ConfigurationManager.AppSettings["PublicKey"];
  22:         SecretKey = ConfigurationManager.AppSettings["SecretKey"];
  23:         Service = new AmazonSimpleDBClient(PublicKey, SecretKey);
  24:     }
  25:  
  26:     public List<string> GetDomains()
  27:     {
  28:         ListDomainsRequest ListDomainsAction = new ListDomainsRequest();
  29:         ListDomainsResponse response = Service.ListDomains(ListDomainsAction);
  30:         return response.ListDomainsResult.DomainName;
  31:     }
  32:  
  33:     public void AddDomain(string domainName)
  34:     {
  35:         CreateDomainRequest myCreateDomainRequest = new CreateDomainRequest {DomainName = domainName};
  36:         CreateDomainResponse response = Service.CreateDomain(myCreateDomainRequest);
  37:     }
  38:  
  39:     public void DeleteDomain(string domainName)
  40:     {
  41:         DeleteDomainRequest request = new DeleteDomainRequest {DomainName = domainName};
  42:         DeleteDomainResponse response = Service.DeleteDomain(request);
  43:     }
  44: }

This class provides several things, first it sets the public and secret keys for simpleDB, this is basically your authentication to simpleDB, as you can see I’m using an app setting to store this. The next important thing is the AmazonSimpleDBClient property called service, this is how you interact with simpleDB. Last you have the Domain methods, these are used to get what domains you have, add new domains, and finally delete unused domains.

Now we have our proxy lets build something that uses it

   1: public class ContactData
   2: {
   3:     private Proxy simpleDBProxy;
   4:     public Proxy SimpleDBProxy
   5:     {
   6:         get
   7:         {
   8:             if (simpleDBProxy==null)
   9:             {
  10:                 simpleDBProxy = new Proxy();
  11:             }
  12:             return simpleDBProxy;
  13:         }
  14:         set
  15:         {
  16:             simpleDBProxy = value;
  17:         }
  18:     }
  19:     const string DomainName = "Contacts";
  20:  
  21:     public Contacts GetContacts()
  22:     {
  23:         Contacts myContacts = new Contacts();
  24:  
  25:         SelectRequest request = new SelectRequest
  26:         {
  27:             SelectExpression = string.Format("SELECT * FROM {0} ", DomainName)
  28:         };
  29:         SelectResponse response = SimpleDBProxy.Service.Select(request);
  30:  
  31:         var contacts = from item in response.SelectResult.Item
  32:                          select new Contact()
  33:                                     {
  34:                                         Email = item.Attribute.GetValueByName("Email"),
  35:                                         Name = item.Attribute.GetValueByName("Name"),
  36:                                         Phone = item.Attribute.GetValueByName("Phone"),
  37:                                         ID =  item.Name
  38:                                     };
  39:         myContacts.AddRange(contacts);
  40:         return myContacts;
  41:     }
  42:  
  43:     public Contacts GetContactsByName(string contactName)
  44:     {
  45:         Contacts myContacts = new Contacts();
  46:  
  47:         SelectRequest request = new SelectRequest
  48:         {
  49:             SelectExpression = string.Format("SELECT * FROM {0} where Name='{1}' ", DomainName, contactName)
  50:         };
  51:         SelectResponse response = SimpleDBProxy.Service.Select(request);
  52:  
  53:         var contacts = from item in response.SelectResult.Item
  54:                        select new Contact()
  55:                        {
  56:                            Email = item.Attribute.GetValueByName("Email"),
  57:                            Name = item.Attribute.GetValueByName("Name"),
  58:                            Phone = item.Attribute.GetValueByName("Phone"),
  59:                            ID = item.Name
  60:                        };
  61:         myContacts.AddRange(contacts);
  62:         return myContacts;
  63:     }
  64:    
  65:     public bool SaveContact(Contact contact)
  66:     {
  67:         List<ReplaceableAttribute> attributeList = new List<ReplaceableAttribute>
  68:            {
  69:                new ReplaceableAttribute().WithName("Email").WithValue(contact.Email),
  70:                new ReplaceableAttribute().WithName("Name").WithValue(contact.Name),
  71:                new ReplaceableAttribute().WithName("Phone").WithValue(contact.Phone)
  72:            };
  73:         contact.ID = Guid.NewGuid().ToString();
  74:         bool success = false;
  75:         try
  76:         {
  77:             if (!SimpleDBProxy.Domains.Contains(DomainName))
  78:             {
  79:                 SimpleDBProxy.AddDomain(DomainName);
  80:             }
  81:             PutAttributesRequest action = new PutAttributesRequest
  82:               {
  83:                   ItemName = contact.ID,
  84:                   Attribute = attributeList,
  85:                   DomainName = DomainName
  86:               };
  87:             PutAttributesResponse response = SimpleDBProxy.Service.PutAttributes(action);
  88:             success = true;
  89:         }
  90:         catch (Exception requestException)
  91:         {
  92:             success = false;
  93:         }
  94:  
  95:         return success;
  96:     }
  97: }

Here we have a simple example of how to add/get items from simpleDB, one important thing to note is the IDs are GUIDs, this is because the objects don’t get and incrementing index ID you need to get a unique identifier and the easiest way to do that is to just used a GUID.

One thing you will notice is I have added some extension method helpers for getting the data out, this is just a way to simplify getting your data back out.

   1: public static class SimpleDBExtedors
   2: {
   3:    public static string GetValueByName(this IList<Attribute> myAttributes, string name)
   4:    {
   5:        string myValue = (from attribute in myAttributes where attribute.Name == name select attribute.Value).FirstOrDefault();
   6:        string value = string.Empty;
   7:        if (!string.IsNullOrEmpty(myValue))
   8:        {
   9:            value = myValue; 
  10:        }
  11:  
  12:        return value;
  13:    }
  14: }

Final thoughts

SimpleDB has some very interesting abilities, primarily the ability to not have to manage your DB structure, worry about indexes, and all of the other DB stuff most devs don’t want to deal with, you simply take whatever objects you have and put them into the DB, and pull them back out. The cloud has some unknowns(security, reliability, etc.) but i think this is definitely a technology to keep your eye on, and last here is the source for the sample application (source), just keep in mind this sample application isn't done and the source is subject to change.

No comments: