Simple web services gateway

I wanted a way of limiting the number of clients that could call a long running and memory intensive web method which exported or imported several thousand lines of data into my database ....so a colleague of mine suggested semaphores. I implemented a singleton class which looked like code snippet B. It all seemed honkey dorey only that we found it turned out to be a pointless use of semaphores.

Semaphores add value when there was a requirement to queue up threads so that if there were 10 threads trying to access the service via a semaphore which allowed only 3 threads, the other threads will be queued up and will wait until the executing threads completed.

Using this singleton semaphore class to limit the number of clients that could call my long running and memory intensive web method was not fully taking advantage of the queuing ability of semaphores. I was monitoring the count of threads and throwing an exception when the limit I set was reached - which is why I said it was pointless.

One other issue with using semaphores for this purpose is that if the IIS application pool was limited to say 10 threads, and 10 users attempted to call my slow running web method call, the threads will be queued which means that if an 11th thread which wanted to make a really fast web method call tried to make its request, it will get a thread aborted exception because the number of threads allowed for the app pool has been exceeded.

So .......what I did instead was to implement a really simple web services gateway which implemented IDisposable so that when called within a using, the count was incremented and on Disposal of the instance, the count is decremented. I created a static instance of it within my web service and just used it when I needed to. It works like a treat and is dead easy to implement. See code snippet A_1 and code snippet A_2.

================ CODE SNIPPET A_1 =========================

   1: private static WebServicesGateway webServicesGateway = new WebServicesGateway(["noOfThreadsPermittedForWebCall"]); 
   2:  
   3: [WebMethod] 
   4: public byte[] RunMyLongRunningMemoryIntesiveMethod() 
   5: { 
   6:     using (webServicesGateway.GatewayRequest()) 
   7:     { 
   8:  
   9:     //do something 
  10:  
  11:     } 
  12: }

================ CODE SNIPPET A_2 =========================

   1: using System; 
   2: using System.Data; 
   3: using System.Configuration; 
   4: using System.Web; 
   5: using System.Web.Security; 
   6: using System.Web.UI; 
   7: using System.Web.UI.HtmlControls; 
   8: using System.Web.UI.WebControls; 
   9: using System.Web.UI.WebControls.WebParts; 
  10:  
  11: namespace Server 
  12: { 
  13:    /// <summary> 
  14:    /// 
  15:    /// </summary> 
  16:    public class WebServicesGateway 
  17:    { 
  18:       private int maxThreadCount = 3; 
  19:       private static int currentThreadCount; 
  20:       private static Gateway gateway; 
  21:       private object instanceLock = new object(); 
  22:  
  23:       public WebServicesGateway(int threadCount) 
  24:       { 
  25:          maxThreadCount = threadCount; 
  26:          gateway = new Gateway(this); 
  27:       } 
  28:  
  29:       private void DecrementCount() 
  30:       { 
  31:          lock (instanceLock) 
  32:          { 
  33:             currentThreadCount--; 
  34:          } 
  35:       } 
  36:  
  37:       public IDisposable GatewayRequest() 
  38:       { 
  39:          lock (instanceLock) 
  40:          { 
  41:             if (currentThreadCount >= maxThreadCount) 
  42:             { 
  43:                throw new WebServicesGatewayLimitException("Gateway has reached its limit"); 
  44:             } 
  45:             currentThreadCount++; 
  46:          } 
  47:          return gateway; 
  48:       } 
  49:  
  50:       private class Gateway : IDisposable 
  51:       { 
  52:          WebServicesGateway webServicesGateway; 
  53:  
  54:          public Gateway(WebServicesGateway webServicesGateway) 
  55:          { 
  56:             this.webServicesGateway = webServicesGateway; 
  57:          } 
  58:  
  59:          public void Dispose() 
  60:          { 
  61:             this.webServicesGateway.DecrementCount(); 
  62:          } 
  63:       } 
  64:    } 
  65:  
  66:    public class WebServicesGatewayLimitException : Exception 
  67:    { 
  68:       private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 
  69:  
  70:       public WebServicesGatewayLimitException(string message) 
  71:          : base(message) 
  72:       { 
  73:          log.Error(message); 
  74:       } 
  75:       public WebServicesGatewayLimitException(string message, Exception exception) 
  76:          : base(message, exception) 
  77:       { 
  78:          log.Error(message, exception); 
  79:       } 
  80:    } 
  81: }
  82:  

================ CODE SNIPPET B =========================

   1: using System; 
   2: using System.Data; 
   3: using System.Configuration; 
   4: using System.Web; 
   5: using System.Threading; 
   6: using System.Web.Security; 
   7: using System.Web.UI; 
   8: using System.Web.UI.HtmlControls; 
   9: using System.Web.UI.WebControls; 
  10: using System.Web.UI.WebControls.WebParts; 
  11: namespace Server 
  12: { 
  13:    /// <summary> 
  14:    /// 
  15:    /// </summary> 
  16:    public class WebMethodCallLimiter : IDisposable 
  17:    { 
  18:       static WebMethodCallLimiter instance = null; 
  19:       static readonly object instanceLock = new object(); 
  20:       private static Semaphore semaphore = null; 
  21:       private static int currentThreadCount = 0; 
  22:       private static int maxThreadCount = 3;
  23:  
  24:       private WebMethodCallLimiter() 
  25:       { 
  26:         semaphore = new Semaphore(0, maxThreadCount);         
  27:       } 
  28:       public void Pool() 
  29:       {        
  30:          if (currentThreadCount >= maxThreadCount) 
  31:          { 
  32:             throw new SemaphoreReachedLimitException("Semaphore has reached its limit"); 
  33:          }
  34:  
  35:          lock (instanceLock) 
  36:          { 
  37:             currentThreadCount++; 
  38:          } 
  39:          semaphore.WaitOne(); 
  40:       }
  41:  
  42:       public void Dispose() 
  43:       { 
  44:          currentThreadCount--; 
  45:          semaphore.Release(); 
  46:       }
  47:  
  48:       public static WebMethodCallLimiter Instance 
  49:       { 
  50:          get 
  51:          { 
  52:             lock (instanceLock) 
  53:             { 
  54:                if (instance == null) 
  55:                { 
  56:                   instance = new WebMethodCallLimiter(); 
  57:                } 
  58:                return instance; 
  59:             } 
  60:          } 
  61:       }
  62:  
  63:    } 
  64:    public class SemaphoreReachedLimitException : Exception 
  65:    { 
  66:       private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 
  67:       public SemaphoreReachedLimitException(string message) 
  68:          : base(message) 
  69:       { 
  70:          log.Error(message); 
  71:       } 
  72:       public SemaphoreReachedLimitException(string message, Exception exception) 
  73:          : base(message, exception) 
  74:       { 
  75:          log.Error(message, exception); 
  76:       } 
  77:    }
  78:  
  79: }
Deleting TFS builds

If you ever wanted to write a C# utility which allows you to delete more than one tfs build at the same time you'll love me for this....

TFS TeamPlain will allow you to delete one build at a time. Visual Studio's Team Explorer for TFS 2008 now lets you delete multiple builds at the same time ..what's also cool is that you can configure the builds you will like to preserve so say, only store the last 2 failed builds and the last 2 stopped builds but preserve all successful builds.  That way you may never need a utitlty to clean up your builds...

However, like me, you may want to write a utility that cleans up the builds should you wish to preserve more or if you don't have team server 2008.....and perhaps schedule it to run as a service. In any case, what you need to do is....

  • Write a filter which takes in specific search criteria so that you can return and act on builds that match the specified criteria.

List<BuildData> buildsToAdd = new List<BuildData>();
TeamFoundationServer tfs = new TeamFoundationServer(this.tfsDetails.ServerName);
BuildController controller = (BuildController)tfs.GetService(typeof(BuildController));

BuildStore bs = (BuildStore)tfs.GetService(typeof(BuildStore));
BuildData[] builds = bs.GetListOfBuilds(this.tfsDetails.Project, this.tfsDetails.BuildType);

  • Then write the code to delete the builds. I stuck the result in a checkedListBox so that the user was able to select which of the builds from the result he'd like to delete.  

         TeamFoundationServer tfs = new TeamFoundationServer(this.tfsDetails.ServerName);
         BuildController controller = (BuildController)tfs.GetService(typeof(BuildController));
         if (!string.IsNullOrEmpty(this.tfsDetails.Project))
         {
            BuildStore bs = (BuildStore)tfs.GetService(typeof(BuildStore));
            BuildData[] builds = bs.GetListOfBuilds(this.tfsDetails.Project, this.tfsDetails.BuildType);
            foreach (string buildSelected in this.chkListOfBuilds.CheckedItems)
            {
               foreach (BuildData build in builds)
               {
                  if (buildSelected.Contains(build.BuildNumber))
                  {
                     string failure = string.Empty;

                     try
                     {
                        controller.DeleteBuild(build.BuildUri, out failure);
                     }
                     catch (Exception e)
                     {
                        //MessageBox.Show( string.Format("Build delete failed for {0} : {1}\n\n", build.BuildNumber, e.Message));
                     }

Any easy way to populate grab the list of team build types is:

 Project p = (Project)this.cmbProjects.SelectedItem;
         if (p != null && IsValidTfsServerUri(this.txtServer.Text))
         {
            TeamFoundationServer tfs = new TeamFoundationServer(this.txtServer.Text);
            VersionControlServer vcs = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));
            ItemSet itemSet = vcs.GetItems("$/" + p.Name + "/TeamBuildTypes", VersionSpec.Latest,
            RecursionType.OneLevel, DeletedState.NonDeleted, ItemType.Folder);
            foreach (Item item in itemSet.Items)
            {
               string buildType = Path.GetFileName(item.ServerItem);
               if (!buildType.Equals("TeamBuildTypes"))
               {
                  //can add it to a combobox list .....this.cmbBuildTypes.Items.Add(buildType);
               }
            }
            this.cmbBuildTypes.Enabled = true;           
         }

Add Comment Filed Under [ MSBuild ]