Export (0) Print
Expand All

Creating a Custom Operand

The Autoscaling Application Block defines the performanceCounter and queueLength operand types for reactive rules. You can also define custom operands for reactive rules: for example to collect custom data points from your Windows Azure application, such as the number of unprocessed orders in the application.

For more information, see the section "Implementing Custom Operands" in Chapter 5, "Making Tailspin Surveys More Elastic" of the Developer Guide.

After you have created and configured a custom operand, administrators must be able to use the operand when they are creating or editing autoscaling rules. Depending on your environment, administrators might create and edit operands in the default rules XML file, in a custom format in a custom rules store, or through a custom UI.

You package a custom operand in an assembly that you deploy with the Autoscaling Application Block. The assembly must contain code that can deserialize your custom operand from your rules store and return an implementation of the IDataPointsCollector interface.

The following sections describe the three steps to implement and use a custom action:

  • Deserializing Custom Operands. Adding support for the block's configuration infrastructure.
  • Defining a Custom Data Collector. Creating the runtime behavior for the operand.
  • Using Your Custom Operand. Modifying your autoscaling rules to use the custom operand.

Deserializing Custom Operands

The following snippet shows how the AutoscalingRules.xsd file defines the operands element in the rules store. Notice how the schema allows you to use an alternative element in a different namespace in place of the performanceCounter or queueLength elements.

<xs:group name="OperandsGroup">
  <xs:choice>
    <xs:element name="performanceCounter">
      <xs:complexType>
        <xs:attribute name="performanceCounterName" type="xs:string" use="required"/>
        <xs:attribute name="source" type="xs:Name" use="required"/>
        <xs:attributeGroup ref="DataPointsOperandsAttributeGroup"/>
      </xs:complexType>
    </xs:element>
    <xs:element name="queueLength">
      <xs:complexType>
        <xs:attribute name="queue" type="xs:Name" use="required"/>
        <xs:attributeGroup ref="DataPointsOperandsAttributeGroup"/>
      </xs:complexType>
    </xs:element>
    <xs:element name="roleInstanceCount">
      <xs:complexType>
        <xs:attribute name="role" type="xs:Name" use="required"/>
        <xs:attributeGroup ref="DataPointsOperandsAttributeGroup"/>
      </xs:complexType>
    </xs:element>
    <xs:any namespace="##other" processContents="lax"/>
  </xs:choice>
</xs:group>

In the assembly that defines your custom action, you must provide code that can deserialize the content of your custom element.

You can use the schema for your custom namespace to provide validation and IntelliSense functionality in the XML editor that you use to edit your autoscaling rules. The following code snippet shows an example schema for a custom operand element named unprocessedOrders.

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns="http://schemas.microsoft.com/practices/2011/entlib/custom-operand/rules"
           xmlns:r="http://schemas.microsoft.com/practices/2011/entlib/custom-operand/rules"
           targetNamespace="http://schemas.microsoft.com/practices/2011/entlib/custom-operand/rules"
           elementFormDefault="qualified"
           attributeFormDefault="unqualified">

  <xs:element name="unprocessedOrders">
    <xs:complexType>
      <xs:attribute name="alias" type="xs:Name"/>
      <xs:attribute name="timespan" type="xs:time"/>
      <xs:attribute name="aggregate" type="AggregateType"/>
      <xs:attribute name="connectionString"/>
    </xs:complexType>
  </xs:element>

  <xs:simpleType name="AggregateType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="Average"/>
      <xs:enumeration value="Max"/>
      <xs:enumeration value="Min"/>
      <xs:enumeration value="Growth"/>
    </xs:restriction>
  </xs:simpleType>

</xs:schema>

This would enable you to enter a custom operand in the rules store XML file, as shown in the following code snippet.

<operands>
  <performanceCounter alias="CPU_45_RoleAC" ... />
  <performanceCounter alias="CPU_45_RoleB" ... />
  <queueLength alias="QueueB_Length_10M_Avg" .../>
  <unprocessedOrders xmlns="http://schemas.microsoft.com/practices/2011/entlib/custom-operand/rules"
      alias="ordertable" aggregate="Average" connectionString="..."/>
</operands>

The following code snippet shows how you should define the content of your custom operand element. Your custom operand element class must extend the DataPointsParameterElement class, which returns an Operand instance from the CreateOperand method. Use your custom operand element class to define any additional attributes for your operand (the example below shows a connectionString attribute) and to define a GetCollectorsFactory function that creates a collector for your custom data points. You must annotate the class with the XmlRoot attribute, and define its ElementName and Namespace properties to match the expected usage in the rules store XML file.

[XmlRoot(ElementName = "unprocessedOrders", Namespace = "http://schemas.microsoft.com/practices/2011/entlib/custom-operand/rules")]
public class UnprocessedOrdersParameterElement : DataPointsParameterElement
{
    [XmlAttribute("connectionString")]
    public string ConnectionString { get; set; }

    protected override string DataPointName
    {
        get { ... }
    }

    protected override string DataPointType
    {
        get { ... }
    }

    protected override string SourceName
    {
        get { ... }
    }

    protected override Func<IServiceInformationStore, IEnumerable<IDataPointsCollector>> GetCollectorsFactory()
    {
        var connectionString = this.ConnectionString;
        var samplingRate = UnprocessedOrdersDataPointsCollector.SamplingRate;

        return (sis) =>
            new[] 
            { 
                new UnprocessedOrdersDataPointsCollector(
                    sis, 
                    connectionString, 
                    samplingRate)
            };
    }
}

Operand elements always have the following attributes: alias, timespan, and aggregate.

Your data point collection factory function must instantiate an IDataPointsCollector object.

Defining a Custom Data Collector

To define a custom data collector, you must implement the IDataPointsCollector interface. The Collect method returns a collection of data points from your custom source.

public class UnprocessedOrdersDataPointsCollector : IDataPointsCollector
{
    public UnprocessedOrdersDataPointsCollector(
        IServiceInformationStore serviceInformationStore, string connectionString, TimeSpan samplingRate)
    {
        ...
    }

    public TimeSpan SamplingRate
       { 
        get { ... }
    }

    public string Key
    { 
        get { ... }
    }

    public IEnumerable<DataPoint> Collect(DateTimeOffset collectionTime)
    { 
        ...
    }
}

When you implement the Collect method, if you throw any exceptions, they should be of type DataPointsCollectionException. The block will not handle any other type of exception.

Using Your Custom Operand

After you have created the assembly with all of the necessary classes to define your custom action, you must configure the Autoscaling Application Block to load the assembly when the block starts. You can do this using the Enterprise Library configuration tool. For information about using the Enterprise Library configuration tool to configure the Autoscaling Application Block, see the topic "Entering Configuration Information."

The following screenshot shows how to enter the name of an assembly that implements a custom action in the configuration tool.

Hh680912.4159937656462A69982E8DC02DB2F86C(en-us,PandP.50).png

Adding the custom operand

You must include the assembly when you deploy your application to Windows Azure.

Last built: June 7, 2012

Show:
© 2014 Microsoft