<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;DUEGQnY5fCp7ImA9WhBaEEQ.&quot;"><id>tag:blogger.com,1999:blog-18281936</id><updated>2013-05-20T17:53:43.824-07:00</updated><category term="ruby" /><category term="Windows Communication Foundation" /><category term="web application" /><category term="Virtual PC" /><category term="ASP.NET 2.0" /><category term="packaging" /><category term="Microsoft" /><category term="Performance" /><category term="MOSS 2007" /><category term="Messaging" /><category term="encoding" /><category term="Windows Server AppFabric" /><category term="perl" /><category term="Windows Workflow Foundation" /><category term="code syntax" /><category term="AppFabric" /><category term="Silverlight 2.0" /><category term="Clojure" /><category term="Windows Server 2008" /><category term="Oracle" /><category term="Tibco" /><category term="jar" /><category term="big data" /><category term="Windows Presentation Foundation" /><category term="WF" /><category term="f#" /><category term="webparts" /><category term="PowerShell" /><category term="fsharp" /><category term="haskell" /><category term="IDictionary" /><category term="tuning" /><category term="Apache web server" /><category term="nosql" /><category term="JMS" /><category term="eclipse" /><category term="Windows SharePoint Services" /><category term="vSphere" /><category term="JSON" /><category term="Workflow Foundation" /><category term=".NET 3.5" /><category term="PowerCLI" /><category term="xml" /><category term="SharePoint 2007" /><category term="Oracle Coherence" /><category term="Glassfish metro" /><category term="heap" /><category term="java" /><category term="Riak" /><category term="IEnumerable" /><category term="Tibco EMS" /><category term="SharePoint" /><category term="Apache Struts 2.0" /><category term=".NET 3.0" /><category term="xslt" /><category term=".NET Framework" /><category term="Coherence" /><category term="memory" /><category term="SharePoint 2010" /><category term="web services" /><category term="jvm" /><category term="WSS" /><category term="C#" /><category term="webwork" /><category term="Tomcat" /><category term="multilingual" /><category term="WCF" /><category term="VMware" /><category term="color" /><category term="functional programming" /><category term=".NET Functional Programming" /><category term="GC" /><category term="statistics" /><category term="ViewState" /><category term="Don Syme" /><category term="saxon" /><category term="j2ee" /><category term="Petzold" /><category term="WPF" /><category term="reuse" /><category term="R" /><category term=".NET" /><category term="Silverlight" /><category term="InfoPath" /><title>John Liao's  Blog</title><subtitle type="html" /><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://jyliao.blogspot.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>75</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/JohnLiaosBlog" /><feedburner:info uri="johnliaosblog" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;DUEFQ3s_eyp7ImA9WhBbFEw.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-8420641815281898158</id><published>2013-05-12T21:00:00.000-07:00</published><updated>2013-05-12T21:00:12.543-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-05-12T21:00:12.543-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".NET Functional Programming" /><category scheme="http://www.blogger.com/atom/ns#" term="fsharp" /><category scheme="http://www.blogger.com/atom/ns#" term="nosql" /><category scheme="http://www.blogger.com/atom/ns#" term="f#" /><category scheme="http://www.blogger.com/atom/ns#" term="Riak" /><title>Riak Links and Link Walking with F# and CorrugatedIron</title><content type="html">&lt;style&gt;
code {background:#F8F8FF; font-weight:bold; font-size:larger; }

#mytable {
 width: 700px;
 padding: 0;
 margin: 0;
}

caption {
 padding: 0 0 5px 0;
 width: 700px;  
 font: italic 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
 text-align: right;
}

th {
 font: bold 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
 color: #4f6b72;
 border-right: 1px solid #C1DAD7;
 border-bottom: 1px solid #C1DAD7;
 border-top: 1px solid #C1DAD7;
 letter-spacing: 2px;
 text-transform: uppercase;
 text-align: left;
 padding: 6px 6px 6px 12px;
 background: #CAE8EA url(images/bg_header.jpg) no-repeat;
}

th.nobg {
 border-top: 0;
 border-left: 0;
 border-right: 1px solid #C1DAD7;
 background: none;
}

td {
 border-right: 1px solid #C1DAD7;
 border-bottom: 1px solid #C1DAD7;
 background: #fff;
 padding: 6px 6px 6px 12px;
 color: #4f6b72;
}


td.alt {
 background: #F5FAFA;
 color: #797268;
}

th.spec {
 border-left: 1px solid #C1DAD7;
 border-top: 0;
 background: #fff url(images/bullet1.gif) no-repeat;
 font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
}

th.specalt {http://www.blogger.com/post-edit.g?blogID=18281936&amp;postID=4559158457660466923
 border-left: 1px solid #C1DAD7;
 border-top: 0;
 background: #f5fafa url(images/bullet2.gif) no-repeat;
 font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
 color: #797268;
}
&lt;/style&gt;
&lt;p&gt;Continuing my journey through the book &lt;a href="http://www.amazon.com/exec/obidos/ASIN/1934356921/techie2biz-20"&gt;Seven Databases in Seven Weeks&lt;/a&gt;, I explore links and link walking in this blog post.
Riak has the ability to establish one-way relationship between entries via &lt;a href="http://docs.basho.com/riak/1.1.4/references/appendices/concepts/Links/"&gt;Links&lt;/a&gt;, providing some of the capabilities of a graph database (Riak documentation calls it a lightweight graph database).  &lt;a href="http://docs.basho.com/riak/1.1.4/references/appendices/comparisons/Riak-Compared-to-Neo4j/"&gt;Riak documentation&lt;/a&gt; hints that links should be kept low, on the order of dozens, not thousands.  Using the Twitter example from &lt;a href="http://riakhandbook.com"&gt;Riak Handbook&lt;/a&gt;, you can probably easily find out who Don Syme follows and who does those people follows, etc.  But it would be difficult to find all the people that follows Don Syme using Riak's link capability.&lt;/p&gt;
&lt;hr/&gt;
&lt;h3&gt;Adding Links&lt;/h3&gt;
&lt;p&gt;

Here's how you would add links with CorrugatedIron library:&lt;/p&gt;
&lt;pre class="brush: fsharp"&gt;
type Cage = { room : int }

(*
Linking cage 1 to polly via contains, equivalent to doing the following
curl -X PUT http://localhost:8098/riak/cages/1 \
-H "Content-Type: application/json" \
-H "Link: &amp;lt;/riak/animals/polly&amp;gt;; riaktag=\"contains\"" \
-d '{"room" : 101}'
*)
client.Put(
  let cage = new RiakObject("cages","1",{room=101})
  cage.LinkTo("animals","polly","contains")
  cage)

(*
Putting ace in cage 2 and setting cage 2 next to cage 1.
Equivalent to the following sample code:
curl -X PUT http://localhost:8091/riak/cages/2 \
-H "Content-Type: application/json" \
-H "Link:&amp;lt;/riak/animals/ace&amp;gt;;riaktag=\"contains\",&amp;lt;/riak/cages/1&amp;gt;;riaktag=\"next_to\"" \
-d '{"room" : 101}'
*)
// Adding more than one link  
client.Put(
  let cage = new RiakObject("cages","2",{room=101})
  [("animals","ace","contains");("cages","1","next_to")]
  |&gt; List.iter(fun (bucket,key,tag) -&gt; cage.LinkTo(bucket,key,tag))
  cage)  
&lt;/pre&gt;

&lt;pre class="brush: plain"&gt;
&lt;/pre&gt;
&lt;hr/&gt;
&lt;h3&gt;Link Walking&lt;/h3&gt;
&lt;p&gt;With CorrugatedIron, I can use the API to perform link walking and as an extra bonus, I don't have to worry about extracting data from the multipart/mixed mime types.  CorrugateIron library takes care of all of that for us.&lt;/p&gt;
&lt;pre class="brush: fsharp"&gt;
(*
Link walking, equivalent to 
curl http://riakhost1:8098/riak/cages/1/_,_,_
or in the new version:
curl http://riakhost1:8098/buckets/cages/keys/1/_,_,_
*)
 
let results = client.WalkLinks(new RiakObject("cages","1"),
                               [|new RiakLink(null,null,null)|])
         
// Dump results, which returns a list of RiakObject(s)
results.Value

// Since in this particular case, we have only one result, we can do the following
// and get back polly
results.Value.[0].GetObject&amp;lt;Animal&amp;gt;()
&lt;/pre&gt;
&lt;p&gt;Here is the output result for &lt;code&gt;results.Value&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="brush: plain"&gt;
// results.Value
val it : IList&amp;lt;RiakObject&amp;gt; =
  seq
    [CorrugatedIron.Models.RiakObject
       {BinIndexes = dict [];
        Bucket = "animals";
        CharSet = null;
        ContentEncoding = null;
        ContentType = "application/json";
        HasChanged = false;
        IntIndexes = dict [];
        Key = "polly";
        LastModified = 1359758822u;
        LastModifiedUsec = 523420u;
        Links = seq [];
        Siblings = seq [];
        UserMetaData = dict [];
        VTag = "2BTveSKTYDNOZNCiOxyryw";
        VTags = seq ["2BTveSKTYDNOZNCiOxyryw"];
        Value = [|123uy; 32uy; 34uy; 110uy; 105uy; 99uy; 107uy; 110uy; 97uy;
                  109uy; 101uy; 34uy; 32uy; 58uy; 32uy; 34uy; 83uy; 119uy;
                  101uy; 101uy; 116uy; 32uy; 80uy; 111uy; 108uy; 108uy; 121uy;
                  32uy; 80uy; 117uy; 114uy; 101uy; 98uy; 114uy; 101uy; 100uy;
                  34uy; 32uy; 44uy; 32uy; 34uy; 98uy; 114uy; 101uy; 101uy;
                  100uy; 34uy; 32uy; 58uy; 32uy; 34uy; 80uy; 117uy; 114uy;
                  101uy; 98uy; 114uy; 101uy; 100uy; 34uy; 32uy; 125uy|];
        VectorClock = [|107uy; 206uy; 97uy; 96uy; 96uy; 96uy; 204uy; 96uy;
                        202uy; 5uy; 82uy; 28uy; 169uy; 111uy; 239uy; 241uy;
                        7uy; 114uy; 230uy; 184uy; 103uy; 48uy; 37uy; 50uy;
                        230uy; 177uy; 50uy; 60uy; 59uy; 216uy; 112uy; 138uy;
                        47uy; 11uy; 0uy|];}]
      
// results.Value.[0].GetObject&amp;lt;Animal&amp;gt;()
val it : Animal = {nickname = "Sweet Polly Purebred";
                   breed = "Purebred";}     
&lt;/pre&gt;    
&lt;p&gt;There seems to be some limitations with link walking using CorrugatedIron library.  There is a &lt;code&gt;WalkLinks()&lt;/code&gt; method as part &lt;code&gt;RiakClient&lt;/code&gt;, but the input is expecting &lt;code&gt;RiakLink&lt;/code&gt; object which has bucket,key, tag as arguments.  The link spec is expecting bucket, tag, and keep flag as arguments.  I do notice that in the Riak documentation that Link Walking is not available as part of the Protocol Buffers Client (PBC) API, so I'm guessing the &lt;code&gt;WalkLinks()&lt;/code&gt; method in CorrugatedIron is either using HTTP protocol or a modified usage of MapReduce. Since link walking is a special case of MapReduce querying, it may not matter much that CorrugatedIron has some limitations on link walking. One other issue with CorrugatedIron and link walking is that when I add multiple links with the same tag and try to link walking with CorrugatedIron, I do not get back all the links, I only get one of the links.  In order to follow the examples in the book &lt;a href="http://www.amazon.com/exec/obidos/ASIN/1934356921/techie2biz-20"&gt;Seven Databases in Seven Weeks&lt;/a&gt;, I can fall back to using ASP.NET MVC Rest API.&lt;/p&gt;
&lt;p&gt;Link walking using CorrugatedIron library:&lt;/p&gt;
&lt;pre class="brush: fsharp"&gt;
// Link walking, equivalent to 
// curl http://localhost:8098/riak/cages/2/animals,_,_
client.WalkLinks(new RiakObject("cages","2"),
                 [|new RiakLink("animals",null,null)|])
     
// curl http://localhost:8098/riak/cages/2/_,next_to,0/animals,_,_
client.WalkLinks(new RiakObject("cages","2"),
                 [|new RiakLink(null,null,"next_to");
                   new RiakLink("animals",null,null);|])

// I can't seem to specify the keep flag with CorrugatedIron, which keeps
// intermediate results as you walk beyond primary links
// curl http://localhost:8091/riak/cages/2/_,next_to,1/_,_,_     

&lt;/pre&gt;
&lt;p&gt;The book Seven Databases in Seven Weeks is still using the old format for HTTP Link Walking.  The &lt;a href="http://docs.basho.com/riak/latest/references/apis/http/HTTP-Link-Walking/#Example"&gt;new format&lt;/a&gt; is as follows:
&lt;pre class="brush: plain"&gt;
GET /riak/bucket/key/[bucket],[tag],[keep]            # Old format
GET /buckets/bucket/keys/key/[bucket],[tag],[keep]    # New format
&lt;/pre&gt;
&lt;p&gt;Link walking using REST API:&lt;/p&gt;
&lt;pre class="brush: fsharp"&gt;

let riakurl = "http://myriakhost1:8098"
let restClient = new HttpClient()

type LinkWalkSpec = 
    { bucket: string; tag: string;  keep: string; }

    member x.Link = (sprintf "%s,%s,%s" x.bucket x.tag x.keep)

let linkWalker url bucket key (links:LinkWalkSpec list) =
    let baseurl = sprintf "%s/buckets/%s/keys/%s" url bucket key
    let rec buildLinkWalkUrl (linklist:LinkWalkSpec list) baseUrl =
      match linklist with
      | [] -&gt; baseUrl
      | [x] -&gt; sprintf "%s/%s" baseUrl (x.Link)
      | h::t -&gt; let newUrl = sprintf "%s/%s" baseUrl (h.Link)
                buildLinkWalkUrl t newUrl

    buildLinkWalkUrl links baseurl
    |&gt; restClient.GetStringAsync

// Equiv to : curl http://localhost:8091/riak/cages/2/_,next_to,1/_,_,_  
   
[{bucket="_";tag="next_to";keep="1"};{bucket="_";tag="_";keep="_"}] 
|&gt; linkWalker riakurl "cages" "2" 
&lt;/pre&gt;
&lt;p&gt;Link walking results from using REST API:&lt;/p&gt;
&lt;pre class="brush: plain"&gt;
val it : Task&amp;lt;string&amp;gt; =
  System.Threading.Tasks.Task`1[System.String]
    {AsyncState = null;
     CreationOptions = None;
     Exception = null;
     Id = 1;
     IsCanceled = false;
     IsCompleted = false;
     IsFaulted = false;
     Result = "
--CveXyss6PAqBxOOWxeWBCf6eXii
Content-Type: multipart/mixed; boundary=AvYXKrJYDlkeNxh1bQyqDvBAuBF

--AvYXKrJYDlkeNxh1bQyqDvBAuBF
X-Riak-Vclock: a85hYGBgzGDKBVIcqW/v8Qdy5rhnMCUy5rEy9Oi+P8WXBQA=
Location: /buckets/cages/keys/1
Content-Type: application/json
Link: &amp;lt;/buckets/animals/keys/polly&amp;gt;; riaktag="contains", &amp;lt;/buckets/cages&amp;gt;; rel="up"
Etag: 6gyXgkIgzvwBGRRotqHK3b
Last-Modified: Fri, 26 Apr 2013 16:55:40 GMT

{"room":101}
--AvYXKrJYDlkeNxh1bQyqDvBAuBF--

--CveXyss6PAqBxOOWxeWBCf6eXii
Content-Type: multipart/mixed; boundary=SaWqmmho48dzhMDmJy3BVcCWrzu

--SaWqmmho48dzhMDmJy3BVcCWrzu
X-Riak-Vclock: a85hYGBgzGDKBVIcqW/v8Qdy5rhnMCUy5rEyPDvYcIovCwA=
Location: /buckets/animals/keys/polly
Content-Type: application/json; charset=utf-8
Link: &amp;lt;/buckets/animals&amp;gt;; rel="up"
Etag: 2BTveSKTYDNOZNCiOxyryw
Last-Modified: Fri, 01 Feb 2013 22:47:02 GMT

{ "nickname" : "Sweet Polly Purebred" , "breed" : "Purebred" }
--SaWqmmho48dzhMDmJy3BVcCWrzu--

--CveXyss6PAqBxOOWxeWBCf6eXii--
";
     Status = RanToCompletion;}      
&lt;/pre&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/i8KP0_BgR9A" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/8420641815281898158/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=8420641815281898158" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/8420641815281898158?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/8420641815281898158?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/i8KP0_BgR9A/riak-links-and-link-walking-with-f-and.html" title="Riak Links and Link Walking with F# and CorrugatedIron" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>1</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2013/05/riak-links-and-link-walking-with-f-and.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkcAQnc6fSp7ImA9WhBUEkg.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-8952900199118871360</id><published>2013-04-26T06:00:00.000-07:00</published><updated>2013-04-29T08:40:43.915-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-29T08:40:43.915-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="functional programming" /><category scheme="http://www.blogger.com/atom/ns#" term=".NET Functional Programming" /><category scheme="http://www.blogger.com/atom/ns#" term="JSON" /><category scheme="http://www.blogger.com/atom/ns#" term=".NET Framework" /><category scheme="http://www.blogger.com/atom/ns#" term="fsharp" /><category scheme="http://www.blogger.com/atom/ns#" term="nosql" /><category scheme="http://www.blogger.com/atom/ns#" term="f#" /><category scheme="http://www.blogger.com/atom/ns#" term="Riak" /><title>Exploring Riak with F# and CorrugatedIron</title><content type="html">&lt;p&gt;Many thanks to &lt;a href="http://www.blogger.com/profile/09971721667055138594"&gt;David&lt;/a&gt; and &lt;a href="http://buffered.io/"&gt;OJ&lt;/a&gt; for recommending &lt;a href="http://corrugatediron.org/"&gt;CorrugatedIron&lt;/a&gt; .Net Riak client library.  The following blog entry is to document my experiments with Riak CRUD operations using CorrugatedIron.&lt;/p&gt;

&lt;h3&gt;Pinging RIAK&lt;/h3&gt;
&lt;p&gt;I tested CorrugatedIron with F# script and here is the setup code along with testing ping capability:&lt;/p&gt;
&lt;pre class="brush: fsharp"&gt;
// Needed to load the following libraries to get F# script to work
#r @"c:\dev\FsRiak\packages\CorrugatedIron.1.3.0\lib\net40\CorrugatedIron.dll"
#r @"c:\dev\FsRiak\packages\protobuf-net.2.0.0.621\lib\net40\protobuf-net.dll"
#r @"c:\dev\FsRiak\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll"

open CorrugatedIron
open CorrugatedIron.Models
open Newtonsoft.Json
open System

// Setup connections
let cluster = RiakCluster.FromConfig("riakConfig", @"c:\dev\FsRiak\App.config");
let client = cluster.CreateClient();

// Ping the Riak Cluster
client.Ping()
&lt;/pre&gt;

&lt;p&gt;Here is my &lt;code&gt;App.config&lt;/code&gt; file used by CorrugatedIron:&lt;/p&gt;
&lt;pre class="brush: xml"&gt;
&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;configuration&amp;gt;
  &amp;lt;configSections&amp;gt;
    &amp;lt;section name="riakConfig" type="CorrugatedIron.Config.RiakClusterConfiguration, CorrugatedIron" /&amp;gt;
  &amp;lt;/configSections&amp;gt;
  &amp;lt;riakConfig nodePollTime="5000" defaultRetryWaitTime="200" defaultRetryCount="3"&amp;gt;
    &amp;lt;nodes&amp;gt;
      &amp;lt;node name="dev1"  hostAddress="mydevhost-a" pbcPort="8087" restScheme="http" restPort="8098" poolSize="10" /&amp;gt;
      &amp;lt;node name="dev2" hostAddress="mydevhost-b" pbcPort="8087" restScheme="http" restPort="8098" poolSize="10" /&amp;gt;
      &amp;lt;node name="dev3" hostAddress="mydevhost-c" pbcPort="8087" restScheme="http" restPort="8098" poolSize="10" /&amp;gt;
    &amp;lt;/nodes&amp;gt;
  &amp;lt;/riakConfig&amp;gt;
&amp;lt;/configuration&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Here's the result from running ping:&lt;/p&gt;
&lt;pre  class="brush: plain"&gt;
val it : RiakResult = CorrugatedIron.RiakResult {ErrorMessage = null;
                                                 IsSuccess = true;
                                                 ResultCode = Success;}
&lt;/pre&gt;

&lt;h3&gt;Get List of Buckets&lt;/h3&gt;
&lt;p&gt;The following method call gets you the list of buckets along with metadata for the call status:
&lt;pre class="brush: fsharp"&gt;
client.ListBuckets()
&lt;/pre&gt;
&lt;p&gt;This method returns the following &lt;code&gt;RiakResult&lt;/code&gt; object:
&lt;pre  class="brush: plain"&gt;
val it : RiakResult&amp;lt;seq&amp;lt;string&amp;gt;&amp;gt; =
  CorrugatedIron.RiakResult`1[System.Collections.Generic.IEnumerable`1[System.String]]
    {ErrorMessage = null;
     IsSuccess = true;
     ResultCode = Success;
     Value = seq ["photos"; "favs"; "animals"; "cages"; ...];}
&lt;/pre&gt;
&lt;h3&gt;Get Bucket Keys&lt;/h3&gt;
&lt;p&gt;Getting a list of keys for a bucket is also simple:&lt;/p&gt;
&lt;pre class="brush: fsharp"&gt;
client.ListKeys("animals")
&lt;/pre&gt;
&lt;p&gt;For the novice, this library warns you to not to do this in production environments....&lt;/p&gt;
&lt;pre  class="brush: plain"&gt;
*** [CI] -&gt; ListKeys is an expensive operation and should not be used in Production scenarios. ***
val it : RiakResult&amp;lt;seq&amp;lt;string&amp;gt;&amp;gt; =
  CorrugatedIron.RiakResult`1[System.Collections.Generic.IEnumerable`1[System.String]]
    {ErrorMessage = null;
     IsSuccess = true;
     ResultCode = Success;
     Value = seq ["ace"; "polly"];}
&lt;/pre&gt;
&lt;h3&gt;Retrieve Content from Riak&lt;/h3&gt;
&lt;p&gt;Getting a value from Riak is pretty easy with this library:
&lt;pre class="brush: fsharp"&gt;
client.Get("animals","ace")
&lt;/pre&gt;
&lt;p&gt;A dump of the return object shows the actual data plus metadata about the &lt;code&gt;Get&lt;/code&gt; operation:
&lt;pre class="brush: plain"&gt;
val it : RiakResult&amp;lt;Models.RiakObject&amp;gt; =
  CorrugatedIron.RiakResult`1[CorrugatedIron.Models.RiakObject]
    {ErrorMessage = null;
     IsSuccess = true;
     ResultCode = Success;
     Value = CorrugatedIron.Models.RiakObject;}
&lt;/pre&gt;
&lt;p&gt;A deeper dive into the &lt;code&gt;Value&lt;/code&gt; field of &lt;code&gt;RiakResult&lt;/code&gt; object gives the following:&lt;/p&gt;
&lt;pre class="brush: plain"&gt;
val it : Models.RiakObject =
  CorrugatedIron.Models.RiakObject
    {BinIndexes = dict [];
     Bucket = "animals";
     CharSet = null;
     ContentEncoding = null;
     ContentType = "application/json";
     HasChanged = false;
     IntIndexes = dict [];
     Key = "ace";
     LastModified = 1359744019u;
     LastModifiedUsec = 788127u;
     Links = seq [];
     Siblings = seq [];
     UserMetaData = dict [];
     VTag = "7aPFusRQHlQ36ZP6G6GSyE";
     VTags = seq ["7aPFusRQHlQ36ZP6G6GSyE"];
     Value = [|123uy; 32uy; 34uy; 110uy; 105uy; 99uy; 107uy; 110uy; 97uy;
               109uy; 101uy; 34uy; 32uy; 58uy; 32uy; 34uy; 84uy; 104uy; 101uy;
               32uy; 87uy; 111uy; 110uy; 100uy; 101uy; 114uy; 32uy; 68uy;
               111uy; 103uy; 34uy; 32uy; 44uy; 32uy; 34uy; 98uy; 114uy; 101uy;
               101uy; 100uy; 34uy; 32uy; 58uy; 32uy; 34uy; 71uy; 101uy; 114uy;
               109uy; 97uy; 110uy; 32uy; 83uy; 104uy; 101uy; 112uy; 104uy;
               101uy; 114uy; 100uy; 34uy; 32uy; 125uy|];
     VectorClock = [|107uy; 206uy; 97uy; 96uy; 96uy; 96uy; 204uy; 96uy; 202uy;
                     5uy; 82uy; 28uy; 172uy; 90uy; 225uy; 175uy; 2uy; 57uy;
                     59uy; 156uy; 51uy; 152uy; 18uy; 25uy; 243uy; 88uy; 25uy;
                     132uy; 59uy; 26uy; 78uy; 241uy; 101uy; 1uy; 0uy|];}
&lt;/pre&gt;
&lt;p&gt;The data I really wanted is embedded in another &lt;code&gt;Value&lt;/code&gt; field where it is represented as an array of bytes, which is not the final format I want.  I wanted to get back the JSON representation of the data I put in.  In my previous blog, I had rolled my own JSON serializer/deserializer, but now I wanted to leverage the other pieces of library bundle by CorrugateIron, namely &lt;a href="http://james.newtonking.com/pages/json-net.aspx"&gt;Json.NET&lt;/a&gt;.  To do so,I define the &lt;code&gt;Animal&lt;/code&gt; type and use Json.NET serializer/deserializer to convert between the objects and it's corresponding JSON representation&lt;/p&gt;
&lt;pre class="brush: fsharp"&gt;
type Animal =
    { nickname: string; breed: string}

// Getting ace from animals bucket 
client.Get("animals","ace").Value.GetObject&amp;lt;Animal&amp;gt;() 
&lt;/pre&gt;
&lt;h3&gt;Adding Content to Riak&lt;/h3&gt;
&lt;p&gt;Adding content to Riak is pretty easy also, after you define a specific type for Json.NET to serialize the fields of that type:
&lt;pre class="brush: fsharp"&gt;
new RiakObject("animals","delta",{nickname="Snoopy"; breed="Beagle"}) 
|&amp;gt; client.Put
&lt;/pre&gt;
&lt;p&gt;If you dump the RiakResult object and the Value field of RiakResult you get the following:&lt;/p&gt;
&lt;pre class="brush: plain"&gt;

val it : RiakResult&lt;RiakObject&gt; =
  CorrugatedIron.RiakResult`1[CorrugatedIron.Models.RiakObject]
    {ErrorMessage = null;
     IsSuccess = true;
     ResultCode = Success;
     Value = CorrugatedIron.Models.RiakObject;}
  
val it : RiakObject =
  CorrugatedIron.Models.RiakObject
    {BinIndexes = dict [];
     Bucket = "animals";
     CharSet = null;
     ContentEncoding = null;
     ContentType = "application/json";
     HasChanged = false;
     IntIndexes = dict [];
     Key = "delta";
     LastModified = 1366930771u;
     LastModifiedUsec = 271921u;
     Links = seq [];
     Siblings = seq [];
     UserMetaData = dict [];
     VTag = "OvZlH7bsYKdO8zL76QdDY";
     VTags = seq ["OvZlH7bsYKdO8zL76QdDY"];
     Value = [|123uy; 34uy; 110uy; 105uy; 99uy; 107uy; 110uy; 97uy; 109uy;
               101uy; 34uy; 58uy; 34uy; 83uy; 110uy; 111uy; 111uy; 112uy;
               121uy; 34uy; 44uy; 34uy; 98uy; 114uy; 101uy; 101uy; 100uy; 34uy;
               58uy; 34uy; 66uy; 101uy; 97uy; 103uy; 108uy; 101uy; 34uy; 125uy|];
     VectorClock = [|107uy; 206uy; 97uy; 96uy; 96uy; 96uy; 204uy; 96uy; 202uy;
                     5uy; 82uy; 28uy; 169uy; 111uy; 239uy; 241uy; 7uy; 114uy;
                     230uy; 184uy; 103uy; 48uy; 37uy; 50uy; 230uy; 177uy; 50uy;
                     4uy; 27uy; 190uy; 59uy; 197uy; 151uy; 5uy; 0uy|];}

&lt;/pre&gt;

&lt;p&gt;To verify interoperability, I would check the newly added data with curl:&lt;/p&gt;
&lt;pre class="brush: plain"&gt;
$ curl -X GET http://192.168.56.1:8098/riak/animals/delta
$ {"nickname":"Snoopy","breed":"Beagle"}
&lt;/pre&gt;
&lt;h3&gt;Delete Riak Contents&lt;/h3&gt;
&lt;p&gt;Delete content by calling the intuitively named &lt;code&gt;Delete&lt;/code&gt; method:&lt;/p&gt;
&lt;pre class="brush: fsharp"&gt;
client.Delete("animals","delta")
&lt;/pre&gt;
&lt;p&gt;One thing that I wasn't sure is how CorrugatedIron talks to the Riak clusters.  In my simple REST API example, I know exactly which host I'm talking to since I explicitly specified the url. For CorrugatedIron, I configure a pool of connections in the app.config file. I wasn't sure which of the nodes CorrugatedIron was talking to.  I fire up &lt;a href="http://www.wireshark.org/"&gt;Wireshark&lt;/a&gt; and notice that I'm connected to the Riak cluster via port 8087, which is through the Protocol Buffers Client (PBC) interface...which explains the need to load the PBC libraries.  This Protocol Buffers client is something new to me and the bundled &lt;a href="http://code.google.com/p/protobuf-net/"&gt;protobuf-net&lt;/a&gt; library seemed to be the code developed by Google (Many thanks to OJ for correcting me on this, this is NOT a Google library but a library written by &lt;a href="marcgravell.blogspot.com"&gt;Marc Gravell&lt;/a&gt;).  This, in turn led me to look at Google &lt;a href="https://developers.google.com/protocol-buffers/"&gt;Protocol Buffers&lt;/a&gt; .  This little side jaunt into CorrugatedIron library led to the discovery (for me) of a whole new set of network communication protocols.  In any case, after checking Wireshark output, it seems that the requests are spread out to the different nodes and not restricted to a single node.  I'm guessing that there are some builtin load balancer code in CorrugatedIron that sends my request to different nodes in the Riak cluster.&lt;/p&gt;
&lt;p&gt;For the basic CRUD operations on Riak, CorrugatedIron has made it easier to work with Riak then having to come up with my own helper functions. This has been a good start and I hope work through more of the Riak examples from the book &lt;a href="http://www.amazon.com/exec/obidos/ASIN/1934356921/techie2biz-20"&gt;Seven Databases in Seven Weeks&lt;/a&gt; with CorrugatedIron in future blog posts.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/r_z-rvplq-8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/8952900199118871360/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=8952900199118871360" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/8952900199118871360?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/8952900199118871360?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/r_z-rvplq-8/exploring-riak-with-f-and-corrugatediron.html" title="Exploring Riak with F# and CorrugatedIron" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>2</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2013/04/exploring-riak-with-f-and-corrugatediron.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ck8EQXY5eCp7ImA9WhBVEE4.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-7824296947578293104</id><published>2013-04-15T06:00:00.000-07:00</published><updated>2013-04-15T06:00:00.820-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-15T06:00:00.820-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".NET" /><category scheme="http://www.blogger.com/atom/ns#" term="nosql" /><category scheme="http://www.blogger.com/atom/ns#" term="f#" /><category scheme="http://www.blogger.com/atom/ns#" term="big data" /><category scheme="http://www.blogger.com/atom/ns#" term="Riak" /><title>Exploring Riak with F#</title><content type="html">&lt;p&gt;I have embraced the 
&lt;a href="http://memeagora.blogspot.com/2006/12/polyglot-programming.html"&gt;
Polyglot Programming&lt;/a&gt; for quite a while already.  This year, I wanted to 
tackle &lt;a href="http://martinfowler.com/bliki/PolyglotPersistence.html"&gt;Polyglot Persistence&lt;/a&gt;.
RDBMS has dominated my world view of persistence layer with everything else as second class 
citizens.  I thought it was time to expand my world view of persistence layers, especially
with the burgeoning popularity of NoSQL movement.  To begin my exploration, I picked up the book
&lt;a href="http://www.amazon.com/exec/obidos/ASIN/1934356921/techie2biz-20"&gt;
Seven Databases in Seven Weeks&lt;/a&gt; by Eric Redmond and Jim Wilson and started with
the first NoSQL persistence layer in the book, which was &lt;a href="http://basho.com/products/riak-overview/"&gt;Riak&lt;/a&gt;.  
According to the book:&lt;/p&gt;
&lt;blockquote&gt;
Riak is a distributed key-value database where values can be anything-from plain text, JSON, or XML to
images or video clips-all accessible through a simple HTTP interface.
&lt;/blockquote&gt;
&lt;h3&gt;Setting up Riak&lt;/h3&gt;
&lt;p&gt;I deployed riak on 3 servers for testing purposes.  In setting up the Riak clusters,
I ran into the following errors:&lt;/p&gt;
&lt;pre class="brush: plain"&gt;
10:42:57.339 [error] gen_server riak_core_capability terminated with reason: no function clause matching orddict:fetch('riak@192.168.56.1', [
{'riak@127.0.0.1',[{{riak_core,staged_joins},[true,false]},{{riak_core,vnode_routing},[proxy,legacy]},...]}]) line 72
/users/domains/riak/lib/os_mon-2.2.9/priv/bin/memsup: Erlang has closed.
&lt;/pre&gt;
&lt;p&gt;A quick Google search brought up the following link
Googling the web, I got this link: 
&lt;a href="http://blog.alwayshere.info/2012/11/riak-error-genserver-riakcorecapability.html"&gt;
http://blog.alwayshere.info/2012/11/riak-error-genserver-riakcorecapability.html&lt;/a&gt;
I originally started riak with 127.0.0.1 address.  Then I made the modification
to the configuration as documented in &lt;a href="http://docs.basho.com/riak/latest/cookbooks/Basic-Cluster-Setup/"&gt;
http://docs.basho.com/riak/latest/cookbooks/Basic-Cluster-Setup/&lt;/a&gt; in trying to setup my 3 server Riak 
into a cluster.  To fix my error, I had to go to ./data/ring folder and delete everything in there, 
then everything works as expected.&lt;/p&gt;
&lt;p&gt;Most of the examples in the book leveraged curl.  However, I learn best if I tried
to work the examples in another way.  I tried the examples in Clojure, using ClojureWerkz's 
&lt;a href="http://clojureriak.info/"&gt;Welle&lt;/a&gt;.  I liked ClojureWerkz's Welle wrappers to Riak
and would probably use it if I had to develop on Java platform.  I also wanted to work with
Riak from .NET platform and hence I'm using F# to explore Riak.  The following examples where done on 
Visual Studio 2010 with &lt;a href="http://www.asp.net/mvc/mvc4"&gt;ASP.NET MVC 4&lt;/a&gt; installed.  This also gives me a chance to take the REST API in ASP.NET MVC4 for a spin.
&lt;/p&gt;
&lt;h3&gt;Pinging RIAK&lt;/h3&gt;
&lt;p&gt;The very first example in the book is to ping the Riak cluster, here's how
I implemented it in F#&lt;/p&gt;
&lt;pre class="brush: fsharp"&gt;
open System.Net.Http
open System.Threading.Tasks

// My 3 instances of Riak
let riak1_url = "http://192.168.56.1:8098"
let riak2_url = "http://192.168.56.2:8098"
let riak3_url = "http://192.168.56.3:8098"

// Pick one to work with
let riakurl = riak1_url

let client = new HttpClient()

let ping () = client.GetAsync(sprintf "%s/ping" riakurl)

ping()
&lt;/pre&gt;
&lt;p&gt;Running the ping, I would get the following response from F#:&lt;/p&gt;
&lt;pre  class="brush: plain"&gt;
val it : Task&lt;HttpResponseMessage&gt; =
  System.Threading.Tasks.Task`1[System.Net.Http.HttpResponseMessage]
    {AsyncState = null;
     CreationOptions = None;
     Exception = null;
     Id = 4;
     IsCanceled = false;
     IsCompleted = false;
     IsFaulted = false;
     Result = StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
  Vary: Accept-Encoding
  Date: Wed, 30 Jan 2013 22:20:11 GMT
  Server: MochiWeb/1.1
  Server: WebMachine/1.9.0
  Server: (someone had painted it blue)
  Content-Length: 422
  Content-Type: application/json
};
     Status = RanToCompletion;}
&lt;/pre&gt;
&lt;h3&gt;Adding Content to RIAK&lt;/h3&gt;
&lt;p&gt;Let's start by putting some stuff into Riak with the following snippet of code&lt;/p&gt;
&lt;pre class="brush: fsharp"&gt;
let put bucket key content =
    let put_url= sprintf "%s/riak/%s/%s" riakurl bucket key
    client.PutAsync(put_url,content)


let put_html bucket key html =
    let put_url= sprintf "%s/riak/%s/%s" riakurl bucket key
    let content = new StringContent(html)
    content.Headers.ContentType.MediaType &amp;lt;- "text/html"
    put bucket key content


"&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&amp;lt;h1&amp;gt;My latest favorite DB is RIAK&amp;lt;/h1&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;"
|&amp;gt; put_html "favs" "db"
&lt;/pre&gt;
&lt;p&gt;Running the above script gets the following response:&lt;/p&gt;
&lt;pre  class="brush: plain"&gt;
val it : Task&lt;HttpResponseMessage&gt; =
  System.Threading.Tasks.Task`1[System.Net.Http.HttpResponseMessage]
    {AsyncState = null;
     CreationOptions = None;
     Exception = null;
     Id = 5;
     IsCanceled = false;
     IsCompleted = false;
     IsFaulted = false;
     Result = StatusCode: 204, ReasonPhrase: 'No Content', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
  Vary: Accept-Encoding
  Date: Fri, 01 Feb 2013 18:07:24 GMT
  Server: MochiWeb/1.1
  Server: WebMachine/1.9.0
  Server: (someone had painted it blue)
  Content-Length: 0
  Content-Type: text/html; charset=utf-8
};
     Status = RanToCompletion;}
&lt;/pre&gt;
&lt;p&gt;We got the 204 code as explained in the book.  
To test that Riak has stored this new content, 
simply point to any of the Riak instances, (e.g. 
&lt;code&gt;http://192.168.56.3:8098/riak/favs/db&lt;/code&gt;)
with a browser and you should see the webpage that was put into the first Riak server. 
&lt;/p&gt;
&lt;p&gt;
Here's the sample code to put JSON data into Riak:
&lt;/p&gt;
&lt;pre class="brush: fsharp"&gt;
// Simple utility to generate JSON - should really use a real JSON library
let tojson data = 
    data |&amp;gt; Seq.map (fun (k,v) -&amp;gt; sprintf "\"%s\" : \"%s\"" k v)
         |&amp;gt; Seq.reduce (sprintf "%s , %s")
         |&amp;gt; sprintf "{ %s }" 


let put_json bucket key jsondata =
    let content = new StringContent(tojson jsondata)
    content.Headers.ContentType.MediaType &amp;lt;- "application/json"
    put bucket key content

[("nickname","The Wonder Dog"); ("breed","German Shepherd")]
|&amp;gt; put_json "animals" "ace"

&lt;/pre&gt;
&lt;p&gt;Again, you can check that it's stored in Riak by pointing the browser to:
&lt;code&gt;http://192.168.56.3:8098/riak/animals/ace&lt;/code&gt; and you should get back:
&lt;pre  class="brush: plain"&gt;
{ "nickname" : "The Wonder Dog" , "breed" : "German Shepherd" }
&lt;/pre&gt;
&lt;h3&gt;Removing Content from RIAK&lt;/h3&gt;
&lt;p&gt;Here's a snippet of script to remove a content from Riak&lt;/p&gt;
&lt;pre class="brush: fsharp"&gt;

let delete bucket key =
    let delete_url= sprintf "%s/riak/%s/%s" riakurl bucket key 
    client.DeleteAsync(delete_url)

delete "animals" "ace"
&lt;/pre&gt;
&lt;h3&gt;Getting Bucket Keys&lt;/h3&gt;
&lt;p&gt;To get all keys in a bucket&lt;/p&gt;
&lt;pre class="brush: fsharp"&gt;
let get_keys bucket =
    let get_url = sprintf "%s/riak/%s?keys=true" riakurl bucket
    get_url |&amp;gt; client.GetStringAsync

let results = get_keys "animals"
printfn "%s" results.Result
&lt;/pre&gt;
&lt;p&gt;The above script would return the following (reformatted for legibility purposes):&lt;/p&gt;
&lt;pre  class="brush: plain"&gt;
{"props":{"name":"animals",
          "allow_mult":false,
    "basic_quorum":false,
    "big_vclock":50,
    "chash_keyfun":{"mod":"riak_core_util",
                    "fun":"chash_std_keyfun"},
    "dw":"quorum",
    "last_write_wins":false,
    "linkfun":{"mod":"riak_kv_wm_link_walker",
               "fun":"mapreduce_linkfun"},
    "n_val":3,
    "notfound_ok":true,
    "old_vclock":86400,
    "postcommit":[],
    "pr":0,
    "precommit":[],
    "pw":0,
    "r":"quorum",
    "rw":"quorum",
    "small_vclock":50,
    "w":"quorum",
    "young_vclock":20},
    "keys":["ace","polly"]}
&lt;/pre&gt;
&lt;h3&gt;Retrieving Content from Riak&lt;/h3&gt;
&lt;p&gt;To retrieve content:&lt;/p&gt;
&lt;pre class="brush: fsharp"&gt;
let get bucket key = 
    let get_url = sprintf "%s/riak/%s/%s/" riakurl bucket key
    get_url |&amp;gt; client.GetStringAsync

let results = get "animals" "ace"
printfn "%s" results.Result
&lt;/pre&gt;

&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/r-EB3J4Mb18" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/7824296947578293104/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=7824296947578293104" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/7824296947578293104?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/7824296947578293104?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/r-EB3J4Mb18/exploring-riak-with-f.html" title="Exploring Riak with F#" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>3</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2013/04/exploring-riak-with-f.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEMFQHs5eyp7ImA9WhBXEkw.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-8962466962093001670</id><published>2013-03-25T06:00:00.000-07:00</published><updated>2013-03-25T06:00:11.523-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-03-25T06:00:11.523-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="functional programming" /><category scheme="http://www.blogger.com/atom/ns#" term="JMS" /><category scheme="http://www.blogger.com/atom/ns#" term="Clojure" /><category scheme="http://www.blogger.com/atom/ns#" term="Tibco EMS" /><category scheme="http://www.blogger.com/atom/ns#" term="Performance" /><title>Compare Tibco EMS Performance Under Load with Clojure</title><content type="html">&lt;style&gt;
#mytable {
 width: 700px;
 padding: 0;
 margin: 0;
}

caption {
 padding: 0 0 5px 0;
 width: 700px;  
 font: italic 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
 text-align: right;
}

th {
 font: bold 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
 color: #4f6b72;
 border-right: 1px solid #C1DAD7;
 border-bottom: 1px solid #C1DAD7;
 border-top: 1px solid #C1DAD7;
 letter-spacing: 2px;
 text-transform: uppercase;
 text-align: left;
 padding: 6px 6px 6px 12px;
 background: #CAE8EA url(images/bg_header.jpg) no-repeat;
}

th.nobg {
 border-top: 0;
 border-left: 0;
 border-right: 1px solid #C1DAD7;
 background: none;
}

td {
 border-right: 1px solid #C1DAD7;
 border-bottom: 1px solid #C1DAD7;
 background: #fff;
 padding: 6px 6px 6px 12px;
 color: #4f6b72;
 text-align:center;
}


td.alt {
 background: #F5FAFA;
 color: #797268;
 text-align:center;
}

th.spec {
 border-left: 1px solid #C1DAD7;
 border-top: 0;
 background: #fff url(images/bullet1.gif) no-repeat;
 font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
}

th.specalt {http://www.blogger.com/post-edit.g?blogID=18281936&amp;postID=4559158457660466923
 border-left: 1px solid #C1DAD7;
 border-top: 0;
 background: #f5fafa url(images/bullet2.gif) no-repeat;
 font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
 color: #797268;
}
&lt;/style&gt;
&lt;p&gt;We have migrated our Tibco EMS infrastructure from the older Sun V490 servers to the newer  HP ProLiant DL380 G7 servers.  
One of the concerns was how does the new servers perform under load compared with the older servers. I needed to create a load
test that compared the performance of the two different types of servers.  I turned to Clojure to help me put together this load test
and here is the load test code:
&lt;hr/&gt;
&lt;h3&gt;Load Test Script in Clojure&lt;/h3&gt;
&lt;pre class="brush: clojure;"&gt;
; Leveraging Apache Commons to generate the random message
(def msg64 (RandomStringUtils/randomAlphanumeric 64))
(def msg128 (RandomStringUtils/randomAlphanumeric 128))
(def msg256 (RandomStringUtils/randomAlphanumeric 256))
(def msg64k (RandomStringUtils/randomAlphanumeric (* 64 1024)))
(def msg128k (RandomStringUtils/randomAlphanumeric (* 128 1024)))
(def msg256k (RandomStringUtils/randomAlphanumeric (* 256 1024)))
(def msg512k (RandomStringUtils/randomAlphanumeric (* 512 1024)))
(def msg1mb (RandomStringUtils/randomAlphanumeric (* 1024 1024)))
(def persistent-delivery (.intValue (javax.jms.DeliveryMode/PERSISTENT)))


; Send messages
(defn send-messages [server-url user password queue-name data iterations]
  (with-open [connection (-&amp;gt; (TibjmsQueueConnectionFactory. server-url)
            (.createQueueConnection user password))]
    (let [session (.createQueueSession connection false Session/AUTO_ACKNOWLEDGE) 
         queue   (.createQueue session  queue-name)]
      (with-open [sender (.createSender session queue)]
  (dotimes [_ iterations]
          (let [message (.createTextMessage session)]
            (.setJMSDeliveryMode message persistent-delivery)
            (.setText message data)
            (.send sender message)))))))
   

(def test-scenarios (list
 {:label "64 bytes" :msg msg64 :n 5000 }
 {:label "128 bytes" :msg msg128 :n 5000 }
 {:label "256 bytes" :msg msg256 :n 5000 }
 {:label "64KB bytes" :msg msg64k :n 2000 }
 {:label "128KB bytes" :msg msg128k :n 1000 }
 {:label "256KB bytes" :msg msg256k :n 1000 }
 {:label "512KB bytes" :msg msg512k :n 1000 }
 {:label "1MB bytes" :msg msg1mb :n 1000 }))

(doseq [my-test test-scenarios]
  (let [label (:label my-test)
       message (:msg my-test)
       n (:n my-test)]
    (println "\n\n")
 (println (str "Testing for " label " sized messages with n = " n))
 (time (send-messages server-url username password test-queue message n))))

&lt;/pre&gt;
&lt;p&gt;Here's the message consumer part of the load test:&lt;/p&gt;
&lt;hr/&gt;
&lt;h3&gt;Message Consumer in Clojure&lt;/h3&gt;
&lt;pre class="brush: clojure;"&gt;                
; Consume Queue Text messages asynchronously
(defn get-queue-text-messages [server-url user password queue-name process-message]
    (future
        (with-open [connection (-&amp;gt; (TibjmsQueueConnectionFactory. server-url)
                                   (.createQueueConnection user password))]
            (let [session (.createQueueSession connection false Session/AUTO_ACKNOWLEDGE)
                  queue (.createQueue session  queue-name)]
                (with-open [receiver (.createReceiver session queue)]             
                    (.start connection)
                    (loop [acc 0]                      
                        (process-message (.receive receiver) acc)
                        (recur (+ acc 1))))))))

; Dump just the message id      
(defn dump-message-id [message n]
    (println (str n)))

     
; Create function aliases with connection information embedded                   
(defn consume-messages [queue-name message-processor]
    (get-queue-text-messages  server-url username password queue-name message-processor))
 
; Start consuming messages asynchronously
(consume-messages queueName dump-message-id)     
&lt;/pre&gt;
&lt;p&gt;Here are the test results:&lt;/p&gt;
&lt;hr/&gt;
&lt;h3&gt;Load Test Result on Sun V490&lt;/h3&gt;

&lt;table&gt;&lt;thead&gt;&lt;th&gt;Message Size&lt;/th&gt;&lt;th&gt;Number of Messages&lt;/th&gt;&lt;th&gt;Process Time (ms)&lt;/th&gt;&lt;th&gt;Throughput (msg/s)&lt;/th&gt;&lt;th&gt;Throughput (MB/s)&lt;/th&gt;&lt;/thead&gt;
&lt;tr&gt;
  &lt;td&gt;64&lt;/td&gt;
  &lt;td&gt;5,000&lt;/td&gt;
  &lt;td&gt;2,772.84&lt;/td&gt;
  &lt;td&gt;1,803.21&lt;/td&gt;
  &lt;td&gt;0.11&lt;/td&gt;
 &lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;128&lt;/td&gt;
  &lt;td&gt;5,000&lt;/td&gt;
  &lt;td&gt;1,943.23&lt;/td&gt;
  &lt;td&gt;2,573.04&lt;/td&gt;
  &lt;td&gt;0.31&lt;/td&gt;
 &lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;256&lt;/td&gt;
  &lt;td&gt;5,000&lt;/td&gt;
  &lt;td&gt;1,899.24&lt;/td&gt;
  &lt;td&gt;2,632.63&lt;/td&gt;
  &lt;td&gt;0.64&lt;/td&gt;
 &lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;64K&lt;/td&gt;
  &lt;td&gt;1,000&lt;/td&gt;
  &lt;td&gt;3,020.98&lt;/td&gt;
  &lt;td&gt;331.02&lt;/td&gt;
  &lt;td&gt;20.69&lt;/td&gt;
 &lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;128K&lt;/td&gt;
  &lt;td&gt;1,000&lt;/td&gt;
  &lt;td&gt;4,414.00&lt;/td&gt;
  &lt;td&gt;226.55&lt;/td&gt;
  &lt;td&gt;28.32&lt;/td&gt;
 &lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;256K&lt;/td&gt;
  &lt;td&gt;1,000&lt;/td&gt;
  &lt;td&gt;20,911.61&lt;/td&gt;
  &lt;td&gt;47.82&lt;/td&gt;
  &lt;td&gt;11.96&lt;/td&gt;
 &lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;512K&lt;/td&gt;
  &lt;td&gt;1,000&lt;/td&gt;
  &lt;td&gt;39,213.23&lt;/td&gt;
  &lt;td&gt;25.50&lt;/td&gt;
  &lt;td&gt;12.75&lt;/td&gt;
 &lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;1MB&lt;/td&gt;
  &lt;td&gt;1,000&lt;/td&gt;
  &lt;td&gt;80,337.55&lt;/td&gt;
  &lt;td&gt;12.45&lt;/td&gt;
  &lt;td&gt;12.45&lt;/td&gt;
 &lt;/tr&gt;
&lt;/table&gt;
&lt;hr/&gt;
&lt;h3&gt;Load Test Result on HP ProLiant DL380 G7&lt;/h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;th&gt;Message Size&lt;/th&gt;&lt;th&gt;Number of Messages&lt;/th&gt;&lt;th&gt;Process Time (ms)&lt;/th&gt;&lt;th&gt;Throughput (msg/s)&lt;/th&gt;&lt;th&gt;Throughput (MB/s)&lt;/th&gt;&lt;/thead&gt;
&lt;tr&gt;
  &lt;td&gt;64&lt;/td&gt;
  &lt;td&gt;5,000&lt;/td&gt;
  &lt;td&gt;1,340.22&lt;/td&gt;
  &lt;td&gt;3,730.74&lt;/td&gt;
  &lt;td&gt;0.23&lt;/td&gt;
 &lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;128&lt;/td&gt;
  &lt;td&gt;5,000&lt;/td&gt;
  &lt;td&gt;1,345.18&lt;/td&gt;
  &lt;td&gt;3,716.99&lt;/td&gt;
  &lt;td&gt;0.45&lt;/td&gt;
 &lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;256&lt;/td&gt;
  &lt;td&gt;5,000&lt;/td&gt;
  &lt;td&gt;1,040.59&lt;/td&gt;
  &lt;td&gt;4,804.95&lt;/td&gt;
  &lt;td&gt;1.17&lt;/td&gt;
 &lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;64K&lt;/td&gt;
  &lt;td&gt;1,000&lt;/td&gt;
  &lt;td&gt;2,178.69&lt;/td&gt;
  &lt;td&gt;458.99&lt;/td&gt;
  &lt;td&gt;28.69&lt;/td&gt;
 &lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;128K&lt;/td&gt;
  &lt;td&gt;1,000&lt;/td&gt;
  &lt;td&gt;3,553.02&lt;/td&gt;
  &lt;td&gt;281.45&lt;/td&gt;
  &lt;td&gt;35.18&lt;/td&gt;
 &lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;256K&lt;/td&gt;
  &lt;td&gt;1,000&lt;/td&gt;
  &lt;td&gt;7,490.54&lt;/td&gt;
  &lt;td&gt;133.50&lt;/td&gt;
  &lt;td&gt;33.38&lt;/td&gt;
 &lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;512K&lt;/td&gt;
  &lt;td&gt;1,000&lt;/td&gt;
  &lt;td&gt;35,078.26&lt;/td&gt;
  &lt;td&gt;28.51&lt;/td&gt;
  &lt;td&gt;14.25&lt;/td&gt;
 &lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;1MB&lt;/td&gt;
  &lt;td&gt;1,000&lt;/td&gt;
  &lt;td&gt;77,076.51&lt;/td&gt;
  &lt;td&gt;12.97&lt;/td&gt;
  &lt;td&gt;12.97&lt;/td&gt;
 &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;From this test result, we can conclude that HP ProLiant DL380 G7 performed better than Sun V490 under load.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/B4sOLML_lIQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/8962466962093001670/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=8962466962093001670" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/8962466962093001670?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/8962466962093001670?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/B4sOLML_lIQ/compare-tibco-ems-performance-under.html" title="Compare Tibco EMS Performance Under Load with Clojure" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>1</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2013/03/compare-tibco-ems-performance-under.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0EFRXc6fCp7ImA9WhBRFE0.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-2538625531843012823</id><published>2013-03-04T06:00:00.000-08:00</published><updated>2013-03-04T06:00:14.914-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-03-04T06:00:14.914-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".NET Functional Programming" /><category scheme="http://www.blogger.com/atom/ns#" term="VMware" /><category scheme="http://www.blogger.com/atom/ns#" term="PowerCLI" /><category scheme="http://www.blogger.com/atom/ns#" term="f#" /><category scheme="http://www.blogger.com/atom/ns#" term="vSphere" /><category scheme="http://www.blogger.com/atom/ns#" term="Performance" /><title>Retrieving VMware vSphere Performance Metrics Catalog with F#</title><content type="html">&lt;p&gt;vSphere collects a lot of performance metrics data on a VMware cluster.
Our problem was that we cannot find a complete documentation on what are all those
performance metrics data collected and what are the units of the collected metric data.
We have asked VMware professional services engineer about this and his response
was that VMware does not have the metrics data documented.&lt;/p&gt;
&lt;p&gt;However, in Chapter 16 of the &lt;a href="http://pubs.vmware.com/vsphere-50/topic/com.vmware.ICbase/PDF/vsdk_prog_guide.pdf"&gt;
vSphere Web Services Programming Guide&lt;/a&gt;, it talked about the fact 
that you can retrieve the metrics catalog from vSphere itself. 
Although this document talks about retrieving the metrics catalog via vSphere
Web Services SDK, you can also retrieve the metrics catalog with PowerCLI.
Below is a F# script that retrieves vSphere metrics catalog:&lt;/p&gt;


&lt;pre class="brush: fsharp;"&gt;
#r @"C:\pkg\VMware\Infrastructure\vSphere PowerCLI\VMware.Vim.dll"

open System
open VMware.Vim
open System.Collections.Specialized

let client = new VimClient()
let service = client.Connect(serviceUrl)

// Must login to do anything - if you are getting null values, it means the session automatically timed out
client.Login(userId,password)

let printMetricCatalog () =
 // counterId max is arbitrarily set, I tried other values
 // and still produced the same number of returned metrics
    let counterIds = seq {1 .. 1000}
    let perfMgr = new PerformanceManager(client,client.ServiceContent.PerfManager)
    let metrics = perfMgr.QueryPerfCounter(counterIds |&amp;gt; Seq.toArray)

    let dumpAllMetricData (metric:PerfCounterInfo) = 
        printfn "-------------------------------------------------------------------\n"
        printfn "Key             : %i" metric.Key
        printfn "Level           : %A" metric.Level
        printfn "PerDeviceLevel  : %A" metric.PerDeviceLevel
        printfn "RollupType      : %A" metric.RollupType
        printfn "StatsType       : %A" metric.StatsType
        printfn "Group.Label     : %s" metric.GroupInfo.Label
        printfn "Group.Key       : %s" metric.GroupInfo.Key
        printfn "Group.Summary   : %s" metric.GroupInfo.Summary
        printfn "NameInfo.Label  : %s" metric.NameInfo.Label
        printfn "NameInfo.Key    : %s" metric.NameInfo.Key
        printfn "NameInfo.Summary: %s" metric.NameInfo.Summary
        printfn "UnitInfo.Label  : %s" metric.UnitInfo.Label
        printfn "UnitInfo.Key    : %s" metric.UnitInfo.Key
        printfn "UnitInfo.Summary: %s" metric.UnitInfo.Summary


    let dumpMetricData (metric:PerfCounterInfo) = printfn "\t%s (%s) - %A stat,  rollup: %A" 
                                                          (metric.NameInfo.Label) 
                                                          (metric.UnitInfo.Label) 
                                                          (metric.StatsType)
                                                          (metric.RollupType)

    // Break metrics catalog by metric groups
    let metricGroups = metrics |&amp;gt; Seq.map (fun metric -&amp;gt; metric.GroupInfo.Label) |&amp;gt; Set.ofSeq |&amp;gt; Set.toSeq

    // Dump short version of metrics catalog
    metricGroups
    |&amp;gt; Seq.iter (fun group -&amp;gt;  printfn "\n--------------------------------------------------"
                               printfn " Metric Group : %s" group
                               printfn "--------------------------------------------------"
                               metrics
                               |&amp;gt; Seq.filter (fun metric -&amp;gt; metric.GroupInfo.Label = group)
                               |&amp;gt; Seq.iter dumpMetricData)

    // Dump complete version of metrics catalog
    metrics |&amp;gt; Seq.iter dumpAllMetricData

printMetricCatalog()

&lt;/pre&gt;
&lt;p&gt;
Here's a dump of the metrics catalog reformatted to display as HTML tables:
&lt;/p&gt;
&lt;style&gt;
#mytable {
 width: 700px;
 padding: 0;
 margin: 0;
}

caption {
 padding: 0 0 5px 0;
 width: 700px;  
 font: italic 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
 text-align: right;
}

th {
 font: bold 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
 color: #4f6b72;
 border-right: 1px solid #C1DAD7;
 border-bottom: 1px solid #C1DAD7;
 border-top: 1px solid #C1DAD7;
 letter-spacing: 2px;
 text-transform: uppercase;
 text-align: left;
 padding: 6px 6px 6px 12px;
 background: #CAE8EA url(images/bg_header.jpg) no-repeat;
}

th.nobg {
 border-top: 0;
 border-left: 0;
 border-right: 1px solid #C1DAD7;
 background: none;
}

td {
 border-right: 1px solid #C1DAD7;
 border-bottom: 1px solid #C1DAD7;
 background: #fff;
 padding: 6px 6px 6px 12px;
 color: #4f6b72;
}


td.alt {
 background: #F5FAFA;
 color: #797268;
}

th.spec {
 border-left: 1px solid #C1DAD7;
 border-top: 0;
 background: #fff url(images/bullet1.gif) no-repeat;
 font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
}

th.specalt {http://www.blogger.com/post-edit.g?blogID=18281936&amp;postID=4559158457660466923
 border-left: 1px solid #C1DAD7;
 border-top: 0;
 background: #f5fafa url(images/bullet2.gif) no-repeat;
 font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
 color: #797268;
}
&lt;/style&gt;
&lt;h2&gt;VMware vSphere Metrics Catalog&lt;/h2&gt;
&lt;hr/&gt;
&lt;h3&gt;CPU&lt;/h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th&gt;Unit&lt;/th&gt;&lt;th&gt;Stats Type&lt;/th&gt;&lt;th&gt;Rollup&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;CPU Capacity Contention&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Percent of time the VMs on this host are unable to run because they are contending for access to the physical CPU(s)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;CPU Capacity Demand&lt;/td&gt;
&lt;td&gt;MHz&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;The amount of CPU resources VMs on this host would use if there were no CPU contention or CPU limit&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;CPU Capacity Entitlement&lt;/td&gt;
&lt;td&gt;MHz&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;CPU resources devoted by the ESX scheduler to virtual machines and resource pools&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;CPU Capacity Provisioned&lt;/td&gt;
&lt;td&gt;MHz&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Capacity in MHz of the physical CPU cores&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;CPU Capacity Usage&lt;/td&gt;
&lt;td&gt;MHz&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;CPU usage in MHz during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;CPU Core Count Contention&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Time the VM is ready to run, but is unable to run due to co-scheduling constraints&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;CPU Core Count Provisioned&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;The number of physical cores provisioned to the entity&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;CPU Core Count Usage&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;The number of virtual processors running on the host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Co-stop&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Time the VM is ready to run, but is unable to due to co-scheduling constraints&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Core Utilization&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;CPU utilization of the corresponding core (if hyper-threading is enabled) as a percentage during the interval (A core is utilized if either or both of its logical CPUs are utilized)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Core Utilization&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;CPU utilization of the corresponding core (if hyper-threading is enabled) as a percentage during the interval (A core is utilized if either or both of its logical CPUs are utilized)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Core Utilization&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;CPU utilization of the corresponding core (if hyper-threading is enabled) as a percentage during the interval (A core is utilized if either or both of its logical CPUs are utilized)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Core Utilization&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;CPU utilization of the corresponding core (if hyper-threading is enabled) as a percentage during the interval (A core is utilized if either or both of its logical CPUs are utilized)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Demand&lt;/td&gt;
&lt;td&gt;MHz&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;The amount of CPU resources a VM would use if there were no CPU contention or CPU limit&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Entitlement&lt;/td&gt;
&lt;td&gt;MHz&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;CPU resources devoted by the ESX scheduler&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Idle&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Total time that the CPU spent in an idle state&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Latency&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Percent of time the VM is unable to run because it is contending for access to the physical CPU(s)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Max limited&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Time the VM is ready to run, but is not run due to maxing out its CPU limit setting&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Overlap&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Time the VM was interrupted to perform system services on behalf of that VM or other VMs&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ready&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Percentage of time that the virtual machine was ready, but could not get scheduled to run on the physical CPU&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Reserved capacity&lt;/td&gt;
&lt;td&gt;MHz&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Total CPU capacity reserved by virtual machines&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Run&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Time the VM is scheduled to run&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap wait&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;CPU time spent waiting for swap-in&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;System&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Amount of time spent on system processes on each virtual CPU in the virtual machine&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Total&lt;/td&gt;
&lt;td&gt;MHz&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Total amount of CPU resources of all hosts in the cluster&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Total capacity&lt;/td&gt;
&lt;td&gt;MHz&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Total CPU capacity reserved by and available for virtual machines&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Usage&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;CPU usage as a percentage during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Usage&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;CPU usage as a percentage during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Usage&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;CPU usage as a percentage during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Usage&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;CPU usage as a percentage during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Usage in MHz&lt;/td&gt;
&lt;td&gt;MHz&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;CPU usage in megahertz during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Usage in MHz&lt;/td&gt;
&lt;td&gt;MHz&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;CPU usage in megahertz during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Usage in MHz&lt;/td&gt;
&lt;td&gt;MHz&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;CPU usage in megahertz during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Usage in MHz&lt;/td&gt;
&lt;td&gt;MHz&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;CPU usage in megahertz during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Used&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Total CPU usage&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Utilization&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;CPU utilization as a percentage during the interval (CPU usage and CPU utilization may be different due to power management technologies or hyper-threading)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Utilization&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;CPU utilization as a percentage during the interval (CPU usage and CPU utilization may be different due to power management technologies or hyper-threading)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Utilization&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;CPU utilization as a percentage during the interval (CPU usage and CPU utilization may be different due to power management technologies or hyper-threading)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Utilization&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;CPU utilization as a percentage during the interval (CPU usage and CPU utilization may be different due to power management technologies or hyper-threading)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Wait&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Total CPU time spent in wait state&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Worst case allocation&lt;/td&gt;
&lt;td&gt;MHz&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Amount of CPU resources allocated to the virtual machine or resource pool based on the total cluster capacity and the resource configuration of the resource hierarchy&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;hr/&gt;
&lt;h3&gt;Cluster services&lt;/h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th&gt;Unit&lt;/th&gt;&lt;th&gt;Stats Type&lt;/th&gt;&lt;th&gt;Rollup&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;CPU fairness&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Fairness of distributed CPU resource allocation&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Current failover level&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;vSphere HA number of failures that can be tolerated&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Effective CPU resources&lt;/td&gt;
&lt;td&gt;MHz&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Total available CPU resources of all hosts within a cluster&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Effective memory resources&lt;/td&gt;
&lt;td&gt;MB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Total amount of machine memory of all hosts in the cluster that is available for use for virtual machine memory and overhead memory&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Memory fairness&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Aggregate available memory resources of all the hosts within a cluster&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;hr/&gt;
&lt;h3&gt;Datastore&lt;/h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th&gt;Unit&lt;/th&gt;&lt;th&gt;Stats Type&lt;/th&gt;&lt;th&gt;Rollup&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;Average read requests per second&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average number of read commands issued per second to the datastore during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Average write requests per second&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average number of write commands issued per second to the datastore during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Highest latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Highest latency value across all datastores used by the host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Read latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;The average time a read from the datastore takes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Read rate&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Rate of reading data from the datastore&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage DRS datastore bytes read&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Storage DRS datastore bytes read&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage DRS datastore bytes written&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Storage DRS datastore bytes written&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage DRS datastore normalized read latency&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Storage DRS datastore normalized read latency&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage DRS datastore normalized write latency&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Storage DRS datastore normalized write latency&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage DRS datastore outstanding read requests&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Storage DRS datastore outstanding read requests&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage DRS datastore outstanding write requests&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Storage DRS datastore outstanding write requests&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage DRS datastore read I/O rate&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Storage DRS datastore read I/O rate&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage DRS datastore read workload metric&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Storage DRS datastore metric for read workload model&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage DRS datastore write I/O rate&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Storage DRS datastore write I/O rate&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage DRS datastore write workload metric&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Storage DRS datastore metric for write workload model&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage I/O Control aggregated IOPS&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Storage I/O Control aggregated IOPS&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage I/O Control datastore maximum queue depth&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Storage I/O Control datastore maximum queue depth&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage I/O Control normalized latency&lt;/td&gt;
&lt;td&gt;Microsecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Storage I/O Control size-normalized I/O latency&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Write latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;The average time a write to the datastore takes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Write rate&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Rate of writing data to the datastore&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;busResets&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;busResets&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;commandsAborted&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;commandsAborted&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;contention&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;contention&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;usage&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;usage&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;hr/&gt;
&lt;h3&gt;Disk&lt;/h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th&gt;Unit&lt;/th&gt;&lt;th&gt;Stats Type&lt;/th&gt;&lt;th&gt;Rollup&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;Average commands issued per second&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average number of SCSI commands issued per second during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Average read requests per second&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average number of disk reads per second during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Average write requests per second&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average number of disk writes per second during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Bus resets&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Number of SCSI-bus reset commands issued during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Capacity&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Configured size of the datastore&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Command latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average amount of time taken during the collection interval to process a SCSI command issued by the Guest OS to the virtual machine&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Commands issued&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Number of SCSI commands issued during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Commands terminated&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Number of SCSI commands terminated during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Disk SCSI Reservation Conflicts&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Number of SCSI reservation conflicts for the LUN during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Disk SCSI Reservation Conflicts %&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Number of SCSI reservation conflicts for the LUN as a percent of total commands during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Disk Throughput Contention&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average amount of time for an I/O operation to complete&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Disk Throughput Usage&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Aggregated disk I/O rate&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Highest latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Highest latency value across all disks used by the host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Kernel command latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average amount of time, in milliseconds, spent by VMkernel to process each SCSI command&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Kernel read latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average amount of time, in milliseconds, spent by VMKernel to process each SCSI read command&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Kernel write latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average amount of time, in milliseconds, spent by VMKernel to process each SCSI write command&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Maximum queue depth&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Maximum queue depth&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Overhead due to delta disk backings&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Storage overhead of a virtual machine or a datastore due to delta disk backings&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Physical device command latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average amount of time, in milliseconds, to complete a SCSI command from the physical device&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Physical device read latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average amount of time, in milliseconds, to read from the physical device&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Physical device write latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average amount of time, in milliseconds, to write to the physical device&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Queue command latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average amount of time spent in the VMkernel queue, per SCSI command, during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Queue read latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average amount of time spent in the VMkernel queue, per SCSI read command, during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Queue write latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average amount time spent in the VMkernel queue, per SCSI write command, during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Read latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average amount of time taken during the collection interval to process a SCSI read command issued from the Guest OS to the virtual machine&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Read rate&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average number of kilobytes read from the disk each second during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Read requests&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Number of disk reads during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Space actually used&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Amount of space actually used by the virtual machine or the datastore&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Space not shared&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Amount of space associated exclusively with a virtual machine&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Space potentially used&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Amount of storage set aside for use by a datastore or a virtual machine&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Usage&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Aggregated disk I/O rate. For hosts, this metric includes the rates for all virtual machines running on the host during the collection interval.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Usage&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Aggregated disk I/O rate. For hosts, this metric includes the rates for all virtual machines running on the host during the collection interval.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Usage&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Aggregated disk I/O rate. For hosts, this metric includes the rates for all virtual machines running on the host during the collection interval.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Usage&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Aggregated disk I/O rate. For hosts, this metric includes the rates for all virtual machines running on the host during the collection interval.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Write latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average amount of time taken during the collection interval to process a SCSI write command issued by the Guest OS to the virtual machine&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Write rate&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average number of kilobytes written to disk each second during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Write requests&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Number of disk writes during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;contention&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;contention&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;provisioned&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;provisioned&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;usage&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;usage&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;hr/&gt;
&lt;h3&gt;Management agent&lt;/h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th&gt;Unit&lt;/th&gt;&lt;th&gt;Stats Type&lt;/th&gt;&lt;th&gt;Rollup&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;CPU usage&lt;/td&gt;
&lt;td&gt;MHz&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of Service Console CPU usage&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Memory swap in&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of memory that is swapped in for the Service Console&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Memory swap out&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of memory that is swapped out for the Service Console&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Memory swap used&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Sum of the memory swapped by all powered-on virtual machines on the host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Memory used&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of total configured memory that is available for use&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;hr/&gt;
&lt;h3&gt;Memory&lt;/h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th&gt;Unit&lt;/th&gt;&lt;th&gt;Stats Type&lt;/th&gt;&lt;th&gt;Rollup&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;Active&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Amount of memory that is actively used, as estimated by VMkernel based on recently touched memory pages&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Active&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of memory that is actively used, as estimated by VMkernel based on recently touched memory pages&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Active&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Amount of memory that is actively used, as estimated by VMkernel based on recently touched memory pages&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Active&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Amount of memory that is actively used, as estimated by VMkernel based on recently touched memory pages&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Active write&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of memory that is actively being written to by the VM&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Balloon&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Amount of memory allocated by the virtual machine memory control driver (vmmemctl), which is installed with VMware Tools&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Balloon&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of memory allocated by the virtual machine memory control driver (vmmemctl), which is installed with VMware Tools&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Balloon&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Amount of memory allocated by the virtual machine memory control driver (vmmemctl), which is installed with VMware Tools&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Balloon&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Amount of memory allocated by the virtual machine memory control driver (vmmemctl), which is installed with VMware Tools&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Balloon target&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Target value set by VMkernal for the virtual machine's memory balloon size&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Balloon target&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Target value set by VMkernal for the virtual machine's memory balloon size&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Balloon target&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Target value set by VMkernal for the virtual machine's memory balloon size&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Balloon target&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Target value set by VMkernal for the virtual machine's memory balloon size&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Compressed&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of memory compressed by ESX&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Compression rate&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Rate of memory compression for the VM&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Consumed&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Amount of memory consumed by a virtual machine, host, or cluster&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Consumed&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of memory consumed by a virtual machine, host, or cluster&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Consumed&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Amount of memory consumed by a virtual machine, host, or cluster&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Consumed&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Amount of memory consumed by a virtual machine, host, or cluster&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Decompression rate&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Rate of memory decompression for the VM&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Entitlement&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of host physical memory the VM is entitled to, as determined by the ESX scheduler&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Granted&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Amount of machine memory or physical memory that is mapped for a virtual machine or a host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Granted&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of machine memory or physical memory that is mapped for a virtual machine or a host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Granted&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Amount of machine memory or physical memory that is mapped for a virtual machine or a host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Granted&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Amount of machine memory or physical memory that is mapped for a virtual machine or a host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Heap&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;VMkernel virtual address space dedicated to VMkernel main heap and related data&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Heap&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;VMkernel virtual address space dedicated to VMkernel main heap and related data&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Heap&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;VMkernel virtual address space dedicated to VMkernel main heap and related data&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Heap&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;VMkernel virtual address space dedicated to VMkernel main heap and related data&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Heap free&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Free address space in the VMkernel's main heap&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Heap free&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Free address space in the VMkernel's main heap&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Heap free&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Free address space in the VMkernel's main heap&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Heap free&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Free address space in the VMkernel's main heap&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Host cache used for swapping&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Space used for caching swapped pages in the host cache&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Host cache used for swapping&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Space used for caching swapped pages in the host cache&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Host cache used for swapping&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Space used for caching swapped pages in the host cache&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Host cache used for swapping&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Space used for caching swapped pages in the host cache&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Latency&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Percentage of time the VM is waiting to access swapped or compressed memory&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Low free threshold&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Threshold of free host physical memory below which ESX will begin reclaiming memory from VMs through ballooning and swapping&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Memory Capacity Contention&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Percentage of time the VM is waiting to access swapped, compressed, or ballooned memory&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Memory Capacity Entitlement&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of host physical memory the VM is entitled to, as determined by the ESX scheduler&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Memory Capacity Provisioned&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Total amount of memory configured for the VM&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Memory Capacity Usable&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of physical memory available for use by virtual machines on this host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Memory Capacity Usage&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of physical memory actively used&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Memory Consumed by VMs&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of physical memory consumed by VMs on this host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Memory Consumed by userworlds&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of physical memory consumed by userworlds on this host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Memory Reserved Capacity %&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Percent of memory that has been reserved either through VMkernel use, by userworlds, or due to VM memory reservations&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Memory saved by zipping&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Memory (KB) saved due to memory zipping&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Overhead&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Memory (KB) consumed by the virtualization infrastructure for running the VM&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Overhead&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Memory (KB) consumed by the virtualization infrastructure for running the VM&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Overhead&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Memory (KB) consumed by the virtualization infrastructure for running the VM&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Overhead&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Memory (KB) consumed by the virtualization infrastructure for running the VM&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Overhead touched&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Actively touched overhead memory (KB) reserved for use as the virtualization overhead for the VM&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Reserved capacity&lt;/td&gt;
&lt;td&gt;MB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Total amount of memory reservation used by powered-on virtual machines and vSphere services on the host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Reserved overhead&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Memory (KB) reserved for use as the virtualization overhead for the VM&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Shared&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Amount of guest memory that is shared with other virtual machines, relative to a single virtual machine or to all powered-on virtual machines on a host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Shared&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of guest memory that is shared with other virtual machines, relative to a single virtual machine or to all powered-on virtual machines on a host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Shared&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Amount of guest memory that is shared with other virtual machines, relative to a single virtual machine or to all powered-on virtual machines on a host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Shared&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Amount of guest memory that is shared with other virtual machines, relative to a single virtual machine or to all powered-on virtual machines on a host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Shared common&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Amount of machine memory that is shared by all powered-on virtual machines and vSphere services on the host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Shared common&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of machine memory that is shared by all powered-on virtual machines and vSphere services on the host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Shared common&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Amount of machine memory that is shared by all powered-on virtual machines and vSphere services on the host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Shared common&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Amount of machine memory that is shared by all powered-on virtual machines and vSphere services on the host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;State&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;One of four threshold levels representing the percentage of free memory on the host. The counter value determines swapping and ballooning behavior for memory reclamation.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap in&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Amount swapped-in to memory from disk&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap in&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount swapped-in to memory from disk&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap in&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Amount swapped-in to memory from disk&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap in&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Amount swapped-in to memory from disk&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap in from host cache&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Amount of memory swapped-in from host cache&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap in from host cache&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of memory swapped-in from host cache&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap in from host cache&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Amount of memory swapped-in from host cache&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap in from host cache&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Amount of memory swapped-in from host cache&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap in rate&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Rate at which memory is swapped from disk into active memory during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap in rate from host cache&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Rate at which memory is being swapped from host cache into active memory&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap out&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Amount of memory swapped-out to disk&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap out&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of memory swapped-out to disk&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap out&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Amount of memory swapped-out to disk&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap out&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Amount of memory swapped-out to disk&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap out rate&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Rate at which memory is being swapped from active memory to disk during the current interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap out rate to host cache&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Rate at which memory is being swapped from active memory to host cache&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap out to host cache&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Amount of memory swapped-out to host cache&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap out to host cache&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of memory swapped-out to host cache&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap out to host cache&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Amount of memory swapped-out to host cache&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap out to host cache&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Amount of memory swapped-out to host cache&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap target&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Target size for the virtual machine swap file&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap target&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Target size for the virtual machine swap file&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap target&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Target size for the virtual machine swap file&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap target&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Target size for the virtual machine swap file&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap unreserved&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Amount of memory that is unreserved by swap&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap unreserved&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of memory that is unreserved by swap&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap unreserved&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Amount of memory that is unreserved by swap&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap unreserved&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Amount of memory that is unreserved by swap&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap used&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Amount of memory that is used by swap&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap used&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of memory that is used by swap&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap used&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Amount of memory that is used by swap&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swap used&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Amount of memory that is used by swap&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swapped&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Current amount of guest physical memory swapped out to the virtual machine's swap file by the VMkernel&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swapped&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Current amount of guest physical memory swapped out to the virtual machine's swap file by the VMkernel&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swapped&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Current amount of guest physical memory swapped out to the virtual machine's swap file by the VMkernel&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swapped&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Current amount of guest physical memory swapped out to the virtual machine's swap file by the VMkernel&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Total&lt;/td&gt;
&lt;td&gt;MB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Total amount of machine memory of all hosts in the cluster that is available for virtual machine memory (physical memory for use by the Guest OS) and virtual machine overhead memory&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Total capacity&lt;/td&gt;
&lt;td&gt;MB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Total amount of memory reservation used by and available for powered-on virtual machines and vSphere services on the host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Unreserved&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Amount of memory that is unreserved&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Unreserved&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of memory that is unreserved&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Unreserved&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Amount of memory that is unreserved&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Unreserved&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Amount of memory that is unreserved&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Usage&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Memory usage as percentage of total configured or available memory&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Usage&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Memory usage as percentage of total configured or available memory&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Usage&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Memory usage as percentage of total configured or available memory&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Usage&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Memory usage as percentage of total configured or available memory&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Used by VMkernel&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Amount of machine memory used by VMkernel for core functionality, such as device drivers and other internal uses&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Used by VMkernel&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of machine memory used by VMkernel for core functionality, such as device drivers and other internal uses&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Used by VMkernel&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Amount of machine memory used by VMkernel for core functionality, such as device drivers and other internal uses&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Used by VMkernel&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Amount of machine memory used by VMkernel for core functionality, such as device drivers and other internal uses&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Worst case allocation&lt;/td&gt;
&lt;td&gt;MB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Memory allocation as calculated by the VMkernel scheduler based on current estimated demand and reservation, limit, and shares policies set for all virtual machines and resource pools in the host or cluster&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Zero&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Memory that contains 0s only&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Zero&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Memory that contains 0s only&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Zero&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Memory that contains 0s only&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Zero&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Memory that contains 0s only&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Zipped memory&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Memory (KB) zipped&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;swapIn&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;swapIn&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;swapIn&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;swapIn&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;swapIn&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;swapIn&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;swapIn&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;swapIn&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;swapOut&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;swapOut&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;swapOut&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;swapOut&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;swapOut&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;swapOut&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;swapOut&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;swapOut&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;userworld&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;userworld&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;userworld&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;userworld&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;vm&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;vm&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;vm&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;vm&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;vmOvhd&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;vmOvhd&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;vmOvrhd&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;vmOvrhd&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;vmkOvrhd&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;vmkOvrhd&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;vmkOvrhd&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;vmkOvrhd&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;hr/&gt;
&lt;h3&gt;Network&lt;/h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th&gt;Unit&lt;/th&gt;&lt;th&gt;Stats Type&lt;/th&gt;&lt;th&gt;Rollup&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;Broadcast receives&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Number of broadcast packets received during the sampling interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Broadcast transmits&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Number of broadcast packets transmitted during the sampling interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Data receive rate&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average rate at which data was received during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Data receive rate&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average amount of data received per second&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Data transmit rate&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average rate at which data was transmitted during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Data transmit rate&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average amount of data transmitted per second&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Multicast receives&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Number of multicast packets received during the sampling interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Multicast transmits&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Number of multicast packets transmitted during the sampling interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Packet receive errors&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Number of packets with errors received during the sampling interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Packet transmit errors&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Number of packets with errors transmitted during the sampling interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Packets received&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Number of packets received during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Packets transmitted&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Number of packets transmitted during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Receive packets dropped&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Number of receives dropped&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Transmit packets dropped&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Number of transmits dropped&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Unknown protocol frames&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Number of frames with unknown protocol received during the sampling interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Usage&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Network utilization (combined transmit-rates and receive-rates) during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Usage&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Network utilization (combined transmit-rates and receive-rates) during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Usage&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Network utilization (combined transmit-rates and receive-rates) during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Usage&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Network utilization (combined transmit-rates and receive-rates) during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;pNic Packets Received and Transmitted per Second&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average rate of packets received and transmitted per second&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;pNic Throughput Provisioned&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Provisioned pNic I/O Throughput&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;pNic Throughput Usable&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Usable pNic I/O Throughput&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;pNic Throughput Usage for FT&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average pNic I/O rate for FT&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;pNic Throughput Usage for NFS&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average pNic I/O rate for NFS&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;pNic Throughput Usage for VMs&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average pNic I/O rate for VMs&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;pNic Throughput Usage for VR&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average pNic I/O rate for VR&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;pNic Throughput Usage for iSCSI&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average pNic I/O rate for iSCSI&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;pNic Throughput Usage for vMotion&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average pNic I/O rate for vMotion&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;vNic Throughput Contention&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Count of vNic packet drops&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;vNic Throughput Usage&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average vNic I/O rate&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;hr/&gt;
&lt;h3&gt;Power&lt;/h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th&gt;Unit&lt;/th&gt;&lt;th&gt;Stats Type&lt;/th&gt;&lt;th&gt;Rollup&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;Cap&lt;/td&gt;
&lt;td&gt;Watt&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Maximum allowed power usage&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Energy usage&lt;/td&gt;
&lt;td&gt;Joule&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Total energy used since last stats reset&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Host Power Capacity Provisioned&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Current power usage as a percentage of maximum allowed power&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Host Power Capacity Usable&lt;/td&gt;
&lt;td&gt;Watt&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Current maximum allowed power usage&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Power Capacity Usage&lt;/td&gt;
&lt;td&gt;Watt&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Current power usage&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Usage&lt;/td&gt;
&lt;td&gt;Watt&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Current power usage&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;hr/&gt;
&lt;h3&gt;Resource group CPU&lt;/h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th&gt;Unit&lt;/th&gt;&lt;th&gt;Stats Type&lt;/th&gt;&lt;th&gt;Rollup&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;Active (1 min. average)&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;CPU active average over 1 minute&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Active (1 min. peak)&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;CPU active peak over 1 minute&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Active (15 min. average)&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;CPU active average over 15 minutes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Active (15 min. peak)&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;CPU active peak over 15 minutes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Active (5 min. average)&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;CPU active average over 5 minutes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Active (5 min. peak)&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;CPU active peak over 5 minutes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Group CPU sample count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Group CPU sample count&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Group CPU sample period&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Group CPU sample period&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Running (1 min. average)&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;CPU running average over 1 minute&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Running (1 min. peak)&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;CPU running peak over 1 minute&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Running (15 min. average)&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;CPU running average over 15 minutes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Running (15 min. peak)&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;CPU running peak over 15 minutes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Running (5 min. average)&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;CPU running average over 5 minutes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Running (5 min. peak)&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;CPU running peak over 5 minutes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Throttled (1 min. average)&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Amount of CPU resources over the limit that were refused, average over 1 minute&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Throttled (15 min. average)&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Amount of CPU resources over the limit that were refused, average over 15 minutes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Throttled (5 min. average)&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Amount of CPU resources over the limit that were refused, average over 5 minutes&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;hr/&gt;
&lt;h3&gt;Storage adapter&lt;/h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th&gt;Unit&lt;/th&gt;&lt;th&gt;Stats Type&lt;/th&gt;&lt;th&gt;Rollup&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;Average commands issued per second&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average number of commands issued per second by the storage adapter during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Average read requests per second&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average number of read commands issued per second by the storage adapter during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Average write requests per second&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average number of write commands issued per second by the storage adapter during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Highest latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Highest latency value across all storage adapters used by the host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Read latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;The average time a read by the storage adapter takes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Read rate&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Rate of reading data by the storage adapter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage Adapter Number Queued&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;The current number of I/Os that are waiting to be issued&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage Adapter Outstanding I/Os&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;The percent of I/Os that have been issued but have not yet completed&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage Adapter Outstanding I/Os&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;The number of I/Os that have been issued but have not yet completed&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage Adapter Queue Command Latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average amount of time spent in the VMkernel queue, per SCSI command, during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage Adapter Queue Depth&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;The maximum number of I/Os that can be outstanding at a given time&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage Adapter Throughput Contention&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average amount of time for an I/O operation to complete&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage Adapter Throughput Usage&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;The storage adapter's I/O rate&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Write latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;The average time a write by the storage adapter takes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Write rate&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Rate of writing data by the storage adapter&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;hr/&gt;
&lt;h3&gt;Storage path&lt;/h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th&gt;Unit&lt;/th&gt;&lt;th&gt;Stats Type&lt;/th&gt;&lt;th&gt;Rollup&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;Average commands issued per second&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average number of commands issued per second on the storage path during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Average read requests per second&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average number of read commands issued per second on the storage path during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Average write requests per second&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average number of write commands issued per second on the storage path during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Highest latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Highest latency value across all storage paths used by the host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Read latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;The average time a read issued on the storage path takes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Read rate&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Rate of reading data on the storage path&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage Path Bus Resets&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Number of SCSI-bus reset commands issued during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage Path Command Aborts&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Number of SCSI commands aborted during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage Path Throughput Contention&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average amount of time for an I/O operation to complete&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage Path Throughput Usage&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Storage path I/O rate&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Write latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;The average time a write issued on the storage path takes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Write rate&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Rate of writing data on the storage path&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;hr/&gt;
&lt;h3&gt;System&lt;/h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th&gt;Unit&lt;/th&gt;&lt;th&gt;Stats Type&lt;/th&gt;&lt;th&gt;Rollup&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;Disk usage&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Amount of disk space usage for each mount point&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Heartbeat&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Number of heartbeats issued per virtual machine during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Heartbeat&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Number of heartbeats issued per virtual machine during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;OS Uptime&lt;/td&gt;
&lt;td&gt;Second&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Total time elapsed, in seconds, since last operating system boot-up&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource CPU active (1 min. average)&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;CPU active average over 1 minute of the system resource group&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource CPU active (5 min. average)&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;CPU active average over 5 minutes of the system resource group&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource CPU allocation maximum, in MHz&lt;/td&gt;
&lt;td&gt;MHz&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;CPU allocation limit, in MHz, of the system resource group&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource CPU allocation minimum, in MHz&lt;/td&gt;
&lt;td&gt;MHz&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;CPU allocation reservation, in MHz, of the system resource group&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource CPU allocation shares&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;CPU allocation shares of the system resource group&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource CPU maximum limited (1 min.)&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;CPU maximum limited over 1 minute of the system resource group&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource CPU maximum limited (5 min.)&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;CPU maximum limited over 5 minutes of the system resource group&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource CPU running (1 min. average)&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;CPU running average over 1 minute of the system resource group&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource CPU running (5 min. average)&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;CPU running average over 5 minutes of the system resource group&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource CPU usage (Average)&lt;/td&gt;
&lt;td&gt;MHz&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Amount of CPU used by the Service Console and other applications during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource CPU usage (Maximum)&lt;/td&gt;
&lt;td&gt;MHz&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Amount of CPU used by the Service Console and other applications during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource CPU usage (Minimum)&lt;/td&gt;
&lt;td&gt;MHz&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Amount of CPU used by the Service Console and other applications during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource CPU usage (None)&lt;/td&gt;
&lt;td&gt;MHz&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Amount of CPU used by the Service Console and other applications during the interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource memory allocation maximum, in KB&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Memory allocation limit, in KB, of the system resource group&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource memory allocation minimum, in KB&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Memory allocation reservation, in KB, of the system resource group&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource memory allocation shares&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Memory allocation shares of the system resource group&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource memory mapped&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Memory mapped by the system resource group&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource memory overhead&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Overhead memory consumed by the system resource group&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource memory share saved&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Memory saved due to sharing by the system resource group&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource memory shared&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Memory shared by the system resource group&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource memory swapped&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Memory swapped out by the system resource group&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource memory touched&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Memory touched by the system resource group&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Resource memory zero&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Zero filled memory used by the system resource group&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Uptime&lt;/td&gt;
&lt;td&gt;Second&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Total time elapsed, in seconds, since last system startup&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;hr/&gt;
&lt;h3&gt;Virtual disk&lt;/h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th&gt;Unit&lt;/th&gt;&lt;th&gt;Stats Type&lt;/th&gt;&lt;th&gt;Rollup&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;Average number of outstanding read requests&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Average number of outstanding read requests to the virtual disk during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Average number of outstanding write requests&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Average number of outstanding write requests to the virtual disk during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Average read requests per second&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average number of read commands issued per second to the virtual disk during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Average write requests per second&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average number of write commands issued per second to the virtual disk during the collection interval&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Read latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;The average time a read from the virtual disk takes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Read rate&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Rate of reading data from the virtual disk&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Read workload metric&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Storage DRS virtual disk metric for the read workload model&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Virtual Disk Throughput Contention&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average amount of time for an I/O operation to complete&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Virtual Disk Throughput Usage&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Virtual disk I/O rate&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Write latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;The average time a write to the virtual disk takes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Write rate&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Rate of writing data to the virtual disk&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Write workload metric&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Storage DRS virtual disk metric for the write workload model&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;busResets&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;busResets&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;commandsAborted&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;commandsAborted&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;hr/&gt;
&lt;h3&gt;Virtual machine operations&lt;/h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th&gt;Unit&lt;/th&gt;&lt;th&gt;Stats Type&lt;/th&gt;&lt;th&gt;Rollup&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;Storage vMotion count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Number of migrations with Storage vMotion (datastore change operations for powered-on VMs)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;VM clone count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Number of virtual machine clone operations&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;VM create count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Number of virtual machine create operations&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;VM datastore change count (non-powered-on VMs)&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Number of datastore change operations for powered-off and suspended virtual machines&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;VM delete count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Number of virtual machine delete operations&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;VM guest reboot count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Number of virtual machine guest reboot operations&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;VM guest shutdown count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Number of virtual machine guest shutdown operations&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;VM host and datastore change count (non-powered-on VMs)&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Number of host and datastore change operations for powered-off and suspended virtual machines&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;VM host change count (non-powered-on VMs)&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Number of host change operations for powered-off and suspended VMs&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;VM power off count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Number of virtual machine power off operations&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;VM power on count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Number of virtual machine power on operations&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;VM reconfigure count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Number of virtual machine reconfigure operations&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;VM register count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Number of virtual machine register operations&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;VM reset count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Number of virtual machine reset operations&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;VM standby guest count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Number of virtual machine standby guest operations&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;VM suspend count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Number of virtual machine suspend operations&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;VM template deploy count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Number of virtual machine template deploy operations&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;VM unregister count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Number of virtual machine unregister operations&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;vMotion count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;latest&lt;/td&gt;
&lt;td&gt;Number of migrations with vMotion (host change operations for powered-on VMs)&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;hr/&gt;
&lt;h3&gt;vCenter debugging information&lt;/h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th&gt;Unit&lt;/th&gt;&lt;th&gt;Stats Type&lt;/th&gt;&lt;th&gt;Rollup&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;Activation count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Activation operations in vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Activation count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Activation operations in vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Activation count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Activation operations in vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Activation latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;The latency of an activation operation in vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Activation latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;The latency of an activation operation in vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Activation latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;The latency of an activation operation in vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Host sync count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;The number of host sync operations in vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Host sync count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;The number of host sync operations in vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Host sync count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;The number of host sync operations in vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Host sync latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;The latency of a host sync operation in vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Host sync latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;The latency of a host sync operation in vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Host sync latency&lt;/td&gt;
&lt;td&gt;Millisecond&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;The latency of a host sync operation in vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Inventory statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;vCenter inventory statistics&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Inventory statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;vCenter inventory statistics&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Inventory statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;vCenter inventory statistics&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Locking statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;vCenter locking statistics&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Locking statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;vCenter locking statistics&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Locking statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;vCenter locking statistics&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Managed object reference statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Managed object reference counts in vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Managed object reference statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Managed object reference counts in vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Managed object reference statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Managed object reference counts in vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Miscellaneous&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Miscellaneous statistics&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Miscellaneous&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Miscellaneous statistics&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Miscellaneous&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Miscellaneous statistics&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Scoreboard statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;Object counts in vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Scoreboard statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;Object counts in vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Scoreboard statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;Object counts in vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Session statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;The statistics of client sessions connected to vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Session statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;The statistics of client sessions connected to vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Session statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;The statistics of client sessions connected to vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;System statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;The statistics of vCenter as a running system such as thread statistics and heap statistics&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;System statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;The statistics of vCenter as a running system such as thread statistics and heap statistics&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;System statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;The statistics of vCenter as a running system such as thread statistics and heap statistics&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;vCenter LRO statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;vCenter LRO statistics&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;vCenter LRO statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;vCenter LRO statistics&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;vCenter LRO statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;vCenter LRO statistics&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;vCenter service statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;maximum&lt;/td&gt;
&lt;td&gt;vCenter service statistics such as events, alarms, and tasks&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;vCenter service statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;minimum&lt;/td&gt;
&lt;td&gt;vCenter service statistics such as events, alarms, and tasks&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;vCenter service statistics&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;summation&lt;/td&gt;
&lt;td&gt;vCenter service statistics such as events, alarms, and tasks&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;hr/&gt;
&lt;h3&gt;vCenter resource usage information&lt;/h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th&gt;Unit&lt;/th&gt;&lt;th&gt;Stats Type&lt;/th&gt;&lt;th&gt;Rollup&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;CPU privileged&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;CPU used by vCenter in privileged mode&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;CPU process&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Total CPU used by vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;CPU queue length&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Processor queue length on the system where vCenter is running&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;CPU system&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Total system CPU used on the system where vCenter in running&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;CPU user&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;CPU used by vCenter in user mode&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Context switch rate&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Number of context switches per second on the system where vCenter is running&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Disk bytes read rate&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Number of bytes read from the disk per second on the system where vCenter is running&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Disk bytes written rate&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Number of bytes written to the disk per second on the system where vCenter is running&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Disk queue length&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Disk queue length on the system where vCenter is running&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Disk read rate&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Number of disk reads per second on the system where vCenter is running&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Disk write rate&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Number of disk writes per second on the system where vCenter is running&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Network queue length&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Network queue length on the system where vCenter is running&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Network usage&lt;/td&gt;
&lt;td&gt;Percent&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Total network bytes received and sent per second on the system where vCenter is running&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Page fault rate&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Number of page faults per second on the system where vCenter is running&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Physical memory&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Physical memory used by vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Pool non-paged bytes&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Memory pooled for non-paged bytes on the system where vCenter is running&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Pool paged bytes&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Memory pooled for paged bytes on the system where vCenter is running&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Process handles&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Handles used by vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Process threads&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Number of threads used by vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Received packet rate&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Rate of the number of total packets received per second on the system where vCenter is running&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Sent packet rate&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Number of total packets sent per second on the system where vCenter is running&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;System call rate&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Number of systems calls made per second on the system where vCenter is running&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;System threads&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Number of threads on the system where vCenter is running&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Total packet rate&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Number of total packets sent and received per second on the system where vCenter is running&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Virtual memory&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Virtual memory used by vCenter&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;hr/&gt;
&lt;h3&gt;vSphere Replication&lt;/h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th&gt;Unit&lt;/th&gt;&lt;th&gt;Stats Type&lt;/th&gt;&lt;th&gt;Rollup&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;Replication Data Receive Rate&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average amount of data received per second&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Replication Data Transmit Rate&lt;/td&gt;
&lt;td&gt;KBps&lt;/td&gt;
&lt;td&gt;rate&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Average amount of data transmitted per second&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;vSphere Replication VM Count&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;absolute&lt;/td&gt;
&lt;td&gt;average&lt;/td&gt;
&lt;td&gt;Current Number of Replicated VMs&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;

&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/kN6bU2Jfc1k" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/2538625531843012823/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=2538625531843012823" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/2538625531843012823?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/2538625531843012823?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/kN6bU2Jfc1k/retrieving-vmware-vsphere-performance.html" title="Retrieving VMware vSphere Performance Metrics Catalog with F#" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2013/03/retrieving-vmware-vsphere-performance.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEMEQnk-cCp7ImA9WhBSEkU.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-1544430879581784273</id><published>2013-02-19T06:00:00.000-08:00</published><updated>2013-02-19T06:00:03.758-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-02-19T06:00:03.758-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="functional programming" /><category scheme="http://www.blogger.com/atom/ns#" term="VMware" /><category scheme="http://www.blogger.com/atom/ns#" term=".NET" /><category scheme="http://www.blogger.com/atom/ns#" term="fsharp" /><category scheme="http://www.blogger.com/atom/ns#" term="PowerCLI" /><category scheme="http://www.blogger.com/atom/ns#" term="f#" /><category scheme="http://www.blogger.com/atom/ns#" term="PowerShell" /><category scheme="http://www.blogger.com/atom/ns#" term="vSphere" /><title>Exploring VMware vSphere PowerCLI with F#</title><content type="html">&lt;p&gt;I was recently asked to vet some VMware cluster capacity numbers.  
It seemed like a task that might be repeated in the future and I really hate to manually
transcribe the data and compare them. So that means I need to write a script to automate it.
Fortunately, VMware has &lt;a href="http://www.vmware.com/support/developer/PowerCLI/index.html"&gt;vSphere PowerCLI&lt;/a&gt;
for this job.  Unfortunately, the documentation for it was rather sparse.  I also looked
at the book &lt;a href="http://www.amazon.com/exec/obidos/ASIN/0470890797/techie2biz-20"&gt;
VMware vSphere PowerCLI Reference: Automating vSphere Administration&lt;/a&gt;, 
but really did not want to script
in PowerShell simply because I'm not that familiar with PowerShell.  
However, PowerCLI is accessible from .NET also, so I can write my script in F#.&lt;/p&gt;
&lt;p&gt;
&lt;p&gt;The following lines of script simply drills down from &lt;code&gt;Datacenter&lt;/code&gt;
object to the &lt;code&gt;VirtualMachine&lt;/code&gt; object.  Once you grab all the objects,
you can explore the properties of each object.&lt;/p&gt;
&lt;pre class="brush: fsharp"&gt;
#r @"C:\pkg\VMware\Infrastructure\vSphere PowerCLI\VMware.Vim.dll"

open System
open VMware.Vim
open System.Collections.Specialized

let serviceUrl = "https://myVSphereHost/sdk"
let userId = "someUserId"
let password="somePassword"

let client = new VimClient()
let service = client.Connect(serviceUrl)

// Must login to do anything - if you are getting null values, it means the session automatically timed out
client.Login(userId,password)

// Let us get all the datacenters
let dataCenters = client.FindEntityViews(typeof&amp;lt;Datacenter&amp;gt;,null,null,null)

// Drill down into the first datacenter
let dc = dataCenters |&amp;gt; Seq.cast&amp;lt;Datacenter&amp;gt; |&amp;gt; Seq.head

// Get a cluster
let cluster = client.GetView(dc.Parent,null) :?&amp;gt; ClusterComputeResource

// Get the first host in the cluster
let host = client.GetView(cluster.Host |&amp;gt; Seq.head,null) :?&amp;gt; HostSystem

// Get the first VM on the physical host
let vm = client.GetView(host.Vm |&amp;gt; Seq.head,null) :?&amp;gt; VirtualMachine
&lt;/pre&gt;
&lt;p&gt;Let's do something interesting with PowerCLI.  In the following scripts,
I wanted to grab the capacity information at the VMware cluster level
and the combine allocation/utilization info of all the virtual machines hosted on the
VMware cluster.
&lt;pre class="brush: fsharp"&gt;

// Utility function to help use get vSphere Entities
let getEntityViews viewType searchParams =
    match searchParams with
    | Some(searchParams0) -&amp;gt;
        let filters = new NameValueCollection()
        searchParams0 |&amp;gt; Seq.iter (fun (k,v) -&amp;gt; filters.Add(k,v))
        client.FindEntityViews(viewType,null,filters,null) 

    | None -&amp;gt;
        client.FindEntityViews(viewType,null,null,null) 

// Get vSphere Entities with specific properties (reduces returned data)
// Don't know how to define a function with multiple arities in F# - clumsy workaround
let getEntityViews2 viewType searchParams props =
    match searchParams with
    | Some(searchParams0) -&amp;gt;
        let filters = new NameValueCollection()
        searchParams0 |&amp;gt; Seq.iter (fun (k,v) -&amp;gt; filters.Add(k,v))
        client.FindEntityViews(viewType,null,filters,props) 
    | None -&amp;gt;
        client.FindEntityViews(viewType,null,null,props) 


// Get Cluster usage summary!
let getUsageSummary clusterName =

    let clusterProps = [|"Summary"; "Host"|]
    let hostProps = [|"Vm";"Name";"Hardware";"Runtime"|]
    let vmProps = [|"Name";"Config";"Runtime";"Summary";"ResourceConfig"|]

    let toMB memoryInBytes = memoryInBytes / (1024L*1024L)

    // Get cluster - Expect only one result
    printfn "Getting cluster data..."
    let cluster = 
        let filters = Some([("name",clusterName)])
        getEntityViews2 typeof&amp;lt;ClusterComputeResource&amp;gt; filters clusterProps
            |&amp;gt; Seq.cast&amp;lt;ClusterComputeResource&amp;gt;
            |&amp;gt; Seq.head


    // Get all Hosts for this cluster
    printfn "Getting host list..."
    let hostList =
        cluster.Host
        |&amp;gt; Seq.map(fun moRef -&amp;gt; client.GetView(moRef,hostProps))
        |&amp;gt; Seq.cast&amp;lt;HostSystem&amp;gt;
        |&amp;gt; Seq.cache


    printfn "Getting VM list..."
    let vmList =
        hostList
        |&amp;gt; Seq.map (fun host -&amp;gt; host.Vm)
        |&amp;gt; Seq.concat
        |&amp;gt; Seq.map (fun moRef -&amp;gt; client.GetView(moRef,vmProps))
        |&amp;gt; Seq.cast&amp;lt;VirtualMachine&amp;gt;
        |&amp;gt; Seq.cache

    let clusterCores = cluster.Summary.NumCpuCores
    let clusterCPU = cluster.Summary.TotalCpu
    let clusterMemory = cluster.Summary.TotalMemory


    // Utility function to get summation results from selected fields
    let inline total (extractor:VirtualMachine -&amp;gt; Nullable&amp;lt;'b&amp;gt;) =
        vmList
        |&amp;gt; Seq.map extractor
        |&amp;gt; Seq.map (fun x -&amp;gt; x.GetValueOrDefault())
        |&amp;gt; Seq.sum


    printfn "Getting Cluster Summary Info..."
    let cpuUsed        = total (fun vm -&amp;gt; vm.Summary.Config.NumCpu)
    let memoryUsed     = total (fun vm -&amp;gt; vm.Summary.Config.MemorySizeMB)
    let cpuReserved    = total (fun vm -&amp;gt; vm.Summary.Config.CpuReservation)
    let memoryReserved = total (fun vm -&amp;gt; vm.Summary.Config.MemoryReservation)
    let maxMemoryUsage = total (fun vm -&amp;gt; vm.Runtime.MaxMemoryUsage)
    let maxCpuUsage    = total (fun vm -&amp;gt; vm.Runtime.MaxCpuUsage)

    let cpuReservation    = total (fun vm -&amp;gt; vm.ResourceConfig.CpuAllocation.Reservation) 
    let cpuLimit          = total (fun vm -&amp;gt; vm.ResourceConfig.CpuAllocation.Limit) 
    let memoryReservation = total (fun vm -&amp;gt; vm.ResourceConfig.MemoryAllocation.Reservation)
    let memoryLimit       = total (fun vm -&amp;gt; vm.ResourceConfig.MemoryAllocation.Limit) 

    
    printfn "Cluster Name               : %s" clusterName
    printfn "Number of Hosts in Cluster : %i" (Seq.length hostList)
    printfn "Number of VMs in Cluster   : %i" (Seq.length vmList)
    printfn "Cluster Total Cores        : %i" clusterCores
    printfn "Cluster Total CPU (MHz)    : %i" clusterCPU
    printfn "Cluster Total Memory       : %i" clusterMemory
    printfn "Cluster Total Memory (MB)  : %i" (toMB clusterMemory)
    printfn "CPU Used by VMs            : %i" cpuUsed
    printfn "Memory Used by VMs         : %i" memoryUsed
    printfn "CPU Reserved by VMs        : %i" cpuReserved
    printfn "Memory Reserved by VMs     : %i" memoryReserved
    printfn "Max Memory Usage by VMs    : %i" maxMemoryUsage
    printfn "Max CPU Usage by VMs (MHz) : %i" maxCpuUsage
    printfn "Total Allocated CPU Reservations    : %i" cpuReservation
    printfn "Total Allocated CPU Limits          : %i" cpuLimit
    printfn "Total Allocated Memory Reservations : %i" memoryReservation
    printfn "Total Allocated Memory Limits       : %i" memoryLimit
    printfn "Memory Used / Cluster Total Memory  : %f" (double(memoryUsed) / double(toMB clusterMemory))
    printfn "vCPU Allocated / Cluster Total Cores : %f" (float(cpuUsed) / float(clusterCores))    
    printfn "Done!"

// Invoke getUsageSummary to get the summary info on my cluster
getUsageSummary "MyTestCluster"
&lt;/pre&gt;
&lt;p&gt;Sample results:&lt;/p&gt;
&lt;pre class="brush: plain"&gt;
Number of Hosts in Cluster : 5
Number of VMs in Cluster   : 10
Cluster Total Cores        : 120
Cluster Total CPU (MHz)    : 276000
Cluster Total Memory       : 1374369136640
Cluster Total Memory (MB)  : 1310700
CPU Used by VMs            : 38
Memory Used by VMs         : 253952
CPU Reserved by VMs        : 0
Memory Reserved by VMs     : 0
Max Memory Usage by VMs    : 253952
Max CPU Usage by VMs (MHz) : 87400
Total Allocated CPU Reservations    : 0
Total Allocated CPU Limits          : -10
Total Allocated Memory Reservations : 0
Total Allocated Memory Limits       : -10
Memory Used / Cluster Total Memory  : 0.193753
vCPU Allocated / Cluster Total Cores : 0.316667
&lt;/pre&gt;
&lt;p&gt;I wish VMware had PowerCLI class documentation similar to those for the .NET library hosted
on MSDN.  If VMware does have those documentation, I can't seem to find them.  Lack of documentation
has forced me to interactively explore the PowerCLI with F#.  
Thankfully, I can do this in F# and 
shudder at the thought of exploring PowerCLI in C#.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/it2BrabGAOk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/1544430879581784273/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=1544430879581784273" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/1544430879581784273?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/1544430879581784273?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/it2BrabGAOk/exploring-vmware-vsphere-powercli-with-f.html" title="Exploring VMware vSphere PowerCLI with F#" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2013/02/exploring-vmware-vsphere-powercli-with-f.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEcEQn05cCp7ImA9WhBTE04.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-8455277825797824927</id><published>2013-02-08T06:00:00.000-08:00</published><updated>2013-02-08T06:00:03.328-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-02-08T06:00:03.328-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="functional programming" /><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="GC" /><category scheme="http://www.blogger.com/atom/ns#" term="Clojure" /><title>Clojure Script to Convert Java GC log to CSV Format</title><content type="html">&lt;p&gt;My &lt;a href="http://jyliao.blogspot.com/2013/02/java-garbage-collection-log-analysis.html"&gt;
previous blog post&lt;/a&gt; showed how to use R to perform Java GC log analysis. Ajit Joglekar asked that I post
the Clojure script.  Here's the Clojure script that I used to convert GC log to CSV format.  
I have not tested this with a wide variety of GC logs but I do know that this script will not work for CMS or G1GC gc logs.
Modify &lt;code&gt;minor-gc-pattern&lt;/code&gt; and &lt;code&gt;full-gc-pattern&lt;/code&gt; to match the gc log output as needed.
&lt;/p&gt; 

&lt;pre class="brush: clojure"&gt;

(use '[clojure.string :only (join)])  

; Match for pause time "0.1566980 secs]"
(def ^:constant pause-time "([\\d\\.]+) secs\\]")

; Match for Java heap space stat "524288K-&amp;gt;32124K(2009792K)"
(def ^:constant space "(\\d+)K-&amp;gt;(\\d+)K\\((\\d+)K\\)")

; Match for Execution stat "[Times: user=0.24 sys=0.06, real=0.16 secs]"
(def ^:constant exec-stat " \\[Times: user=([\\d\\.]+) sys=([\\d\\.]+), real=([\\d\\.]+) secs\\]")

; Example Minor GC entry   
; 212.785: [GC [PSYoungGen: 524288K-&amp;gt;32124K(611648K)] 524288K-&amp;gt;32124K(2009792K), 0.1566980 secs] [Times: user=0.24 sys=0.06, real=0.16 secs] 
; Define regex pattern to parse young gen GC event
(defn minor-gc-pattern []
  (let [timestamp  "([\\d\\.]+): \\[GC .*\\[PS.*: "
        young-gen   (str space "] ")
        heap        (str space ", ")]
    (re-pattern (str timestamp young-gen  heap pause-time exec-stat))))                

; Example Full GC entry   
;"43587.513: [Full GC (System) [PSYoungGen: 964K-&amp;gt;0K(598912K)] [PSOldGen: 142673K-&amp;gt;120674K(1398144K)] 143637K-&amp;gt;120674K(1997056K) [PSPermGen: 82179K-&amp;gt;82179K(147520K)], 0.7556570 secs] [Times: user=0.76 sys=0.00, real=0.77 secs]"
; Define the regex pattern to parse each line in gc log
(defn full-gc-pattern []
    (let [timestamp   "([\\d\\.]+): \\[Full.*"
          young-gen  (str ": " space "]")
          old-gen    (str " \\[\\w+: " space "\\] ")
          perm-gen   (str " \\[\\w+: "space "\\], ")
          heap       space]
         (re-pattern (str timestamp 
                          young-gen 
                          old-gen 
                          heap 
                          perm-gen 
                          pause-time 
                          exec-stat))))                
 
; Variable definitions (for both process-full-gc &amp; process-minor-gc
;     ts - timestamp (in seconds)
;     ys - YoungGen space starting heap size (in KB)
;     ye - YoungGen space ending heap size (in KB)
;     ym - YoungGen space max heap size (in KB)
;     os - OldGen space starting heap size (in KB)
;     oe - OldGen space ending heap size (in KB)
;     om - OldGen space max heap size (in KB)
;     hs - Total heap space starting heap size (in KB)
;     he - Total heap space ending heap size (in KB)
;     hm - Total heap space max heap size (in KB)
;     pt - GC Pause Time (in seconds)
;     ps - PermGen space starting heap size (in KB)
;     pe - PermGen space ending heap size (in KB)
;     pm - PermGen space max heap size (in KB)
;     ut - User Time (in seconds)
;     kt - Kernel Time (in seconds)
;     rt - Real Time (in seconds)
(defn process-full-gc [entry]
  (let [[a ts ys ye ym os oe om hs he hm ps pe pm pt ut kt rt &amp; e] entry]
    (join \, [ts "full" pt 
              ys ye ym 
              hs he hm 
              ut kt rt 
              os oe om 
              ps pe pm])))
   
(defn process-minor-gc [entry]
  (let [[a ts ys ye ym hs he hm pt ut kt rt &amp; e] entry]
    (join \, [ts "minor" pt 
              ys ye ym 
              hs he hm 
              ut kt rt])))

(def headers (join \,  ["timestamp" "gc.type" "pause.time"
                        "young.start" "young.end" "young.max"
                        "heap.start" "heap.end" "heap.max"
                        "time.user" "time.sys" "time.real"
                        "old.start" "old.end" "old.max"
                        "perm.start" "perm.end" "perm.max"]))    
       

(defn process-gc-file [infile outfile]
  (let [gcdata (line-seq (clojure.java.io/reader (clojure.java.io/file infile)))]
    (with-open [w (clojure.java.io/writer outfile)]
      (let [writeln (fn [x] (.write w (str x "\n")))]
        (writeln headers)
        (doseq [line gcdata]
          (let [minor-gc (re-seq (minor-gc-pattern) line)
                full-gc  (re-seq (full-gc-pattern) line)]
            (when-not (nil? full-gc) 
              (writeln (process-full-gc (first full-gc))))
            (when-not (nil? minor-gc) 
              (writeln (process-minor-gc (first minor-gc))))))))))

;-----------------------------------------------------------------------   
; Convert Java GC log csv format
;-----------------------------------------------------------------------    
(process-gc-file "gc.log" "data.csv")

&lt;/pre&gt;
&lt;p&gt;You can download the script directly here : &lt;a href="https://sites.google.com/site/programminglibrary/scripts/gcanalysis.clj"&gt;gcanalysis.clj&lt;/a&gt;.
&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/gXyRiS-cHCY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/8455277825797824927/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=8455277825797824927" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/8455277825797824927?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/8455277825797824927?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/gXyRiS-cHCY/clojure-script-to-convert-java-gc-log.html" title="Clojure Script to Convert Java GC log to CSV Format" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2013/02/clojure-script-to-convert-java-gc-log.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CE8ERn87eyp7ImA9WhNaGUU.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-7535703335006582839</id><published>2013-02-04T05:00:00.000-08:00</published><updated>2013-02-04T05:00:07.103-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-02-04T05:00:07.103-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="statistics" /><category scheme="http://www.blogger.com/atom/ns#" term="tuning" /><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="GC" /><category scheme="http://www.blogger.com/atom/ns#" term="R" /><category scheme="http://www.blogger.com/atom/ns#" term="Performance" /><category scheme="http://www.blogger.com/atom/ns#" term="jvm" /><title>Java Garbage Collection Log Analysis with R</title><content type="html">&lt;p&gt;In the past, I generally perform Java GC log analysis using a combination of scripts such as the one I have written in my previous &lt;a href="http://jyliao.blogspot.com/2012/06/quick-and-easy-way-to-monitor-for.html"&gt;blog post&lt;/a&gt; for parsing the gc log and transforming it into csv format, &lt;a href="http://java.net/projects/gchisto"&gt;GCHisto&lt;/a&gt; tool as mentioned in the book Java Performance, and Microsoft Excel for mostly plotting purposes.  However, as I find myself doing more and more GC analysis for troubleshooting performance issues, I'm finding that these tools are not flexible enough for my purposes. &lt;/p&gt;
&lt;p&gt;I heard about &lt;a href="http://www.r-project.org/"&gt;The R Project&lt;/a&gt; from one my coworker. In addition, I see R &lt;a href="http://www.revolutionanalytics.com/what-is-open-source-r/companies-using-r.php"&gt;gaining traction&lt;/a&gt; in the industry; I decided to try using R for GC analysis.  This blog post is to document some of my experiences with using R for GC analysis.&lt;/p&gt;
&lt;p&gt;There are some posts in the blogosphere that seem to suggest that it is difficult to learn R.  I did not find that to be the case.  Whether that's due to knowing functional programming or past experience with Matlab, I don't know.  I did purchase a bunch of books on R to learn it including
&lt;a href="http://www.amazon.com/exec/obidos/ASIN/144931208X/techie2biz-20"&gt;R in a Nutshell&lt;/a&gt;,
&lt;a href="http://www.amazon.com/exec/obidos/ASIN/1461413648/techie2biz-20"&gt;R by Example&lt;/a&gt;,
&lt;a href="http://www.amazon.com/exec/obidos/ASIN/1935182390/techie2biz-20"&gt;R in Action&lt;/a&gt;,
&lt;a href="http://www.amazon.com/exec/obidos/ASIN/0387790535/techie2biz-20"&gt;Introductory Statistics with R&lt;/a&gt;.  
I got the most mileage with the last book.&lt;/p&gt;
&lt;p&gt;In working with R, I still would use a script to parse out raw GC log and converted into csv file.  So far, most of my GC analysis are on non-CMS GC logs so this blog post is restricted to discussing analysis of mostly parallel GC logs.  In order to be able to better read my R scripts, I have changed my script to output the csv file with the following headers:
&lt;pre class="brush: plain"&gt;
timestamp,gc.type,young.start,young.end,young.max,heap.start,heap.end,heap.max,pause.time,old.start,old.end,old.max,perm.start,perm.end,perm.max,time.user,time.sys,time.real
&lt;/pre&gt;
&lt;p&gt;I can load the csv file in R as follows:&lt;/p&gt;
&lt;pre class="brush: r"&gt;
data.file &amp;lt;- "gc.log.csv"
data.gc &amp;lt;- read.csv(data.file,sep=',')
&lt;/pre&gt;
&lt;p&gt;I created the following utility functions because I find that I often want to work in  MB or GB for heap space rather than in KB.  Also, I want to work with timestamp in hours rather than seconds.&lt;/p&gt;
&lt;pre class="brush: r"&gt;
sec.to.hours  &amp;lt;- function(x) x/(60.0*60.0)
kb.to.mb &amp;lt;- function(x) x/1024.0
kb.to.gb &amp;lt;- function(x) x/1024.0^2
&lt;/pre&gt;
&lt;h3&gt;Printing GC Pause Time Statistics&lt;/h3&gt;
&lt;p&gt;With this, I can dump the GC pause time statistical information as follows:&lt;/p&gt;
&lt;pre class="brush: r"&gt;
# Dump mean, standard deviation, GC counts for GC pause time
attach(data.gc)
 by(pause.time,gc.type,mean)
 by(pause.time,gc.type,sd)
 by(pause.time,gc.type,length)

 # GC Frequency statistics
 summary(diff(timestamp))

 # Time spent in GC
 sum(pause.time)

 # Percentage of time spent in GC
 sum(pause.time)*100.0/max(timestamp)
 
detach(data.gc)
&lt;/pre&gt;
&lt;p&gt;For the above code, a sample output would be:&lt;/p&gt;
&lt;pre class="brush: plain"&gt;
# by(pause.time,gc.type,mean) output 
# GC Average Pause Time
gc.type: full
[1] 2.125116
-------------------------------------------------------------- 
gc.type: minor
[1] 0.06472383

# by(pause.time,gc.type,sd) output 
# GC pause time distribution
gc.type: full
[1] 1.01972
-------------------------------------------------------------- 
gc.type: minor
[1] 0.02684703

# by(pause.time,gc.type,length) output 
# GC count by type
gc.type: full
[1] 7
-------------------------------------------------------------- 
gc.type: minor
[1] 2172

# summary(diff(timestamp)) output 
# GC Frequency statistics - (e.g. GC occurs on average every 84 seconds)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   0.08   22.44   71.34   84.83  143.10  252.90 
   
# sum(pause.time) output
# Total time spent in GC   
[1] 155.456

# sum(pause.time)*100.0/max(timestamp) output 
# Percentage of time spent in GC
[1] 0.08412511
&lt;/pre&gt;
&lt;h3&gt;Plotting GC Timeline&lt;/h3&gt;
&lt;p&gt;Here's how to plot GC Timeline:&lt;/p&gt;
&lt;pre class="brush: r"&gt;
with(data.gc, {
 plot.title &amp;lt;-"GC Timeline"
 gc.minor &amp;lt;- pause.time[gc.type == "minor"]
 gc.full &amp;lt;- pause.time[gc.type == "full"]

 # Plot bars for full GC pause time 
 plot(sec.to.hours(timestamp[gc.type == "full"]),
   gc.full*1000.0,
   xlab="Elapsed Time (hrs)",
   ylab="GC Pause Time Time(ms)",type='h',
   main=plot.title,col="red",ylim=c(0,1000),xlim=c(0,18))
   
 # Plot bars for minor GC pause time
 lines(sec.to.hours(timestamp[gc.type == "minor"]),
       gc.minor*1000.0,col="dark green",type="h")

 # Set tick marks for every hour
 axis(1,seq(0,18,1))
 grid()
})
&lt;/pre&gt;
&lt;p&gt;A sample output of the above code would be:&lt;/p&gt;
&lt;a href="http://4.bp.blogspot.com/-oTXTf2rVJ-Y/UQrlfssJjkI/AAAAAAAAALo/Ibr7e-nfZVM/s1600/gctimeline.png" imageanchor="1" style=""&gt;&lt;img border="0" height="178" width="400" src="http://4.bp.blogspot.com/-oTXTf2rVJ-Y/UQrlfssJjkI/AAAAAAAAALo/Ibr7e-nfZVM/s400/gctimeline.png" /&gt;&lt;/a&gt;
&lt;h3&gt;Plotting GC Pause Time Distribution&lt;/h3&gt;
&lt;p&gt;To plot GC pause time distribution:&lt;/p&gt;
&lt;pre class="brush: r"&gt;
# Plot GC Pause Time Distribution
plot.title &amp;lt;- "GC Pause Time Distribution"
with(data.gc, {
 hist(pause.time,breaks=100, 
   xlab="GC Pause Time(sec)",
   xlim=c(0,1.0),
   main=plot.title)
})

&lt;/pre&gt;
&lt;a href="http://1.bp.blogspot.com/-TWREIARHndQ/UQrlnkuuTpI/AAAAAAAAAL0/JKXacJy-_EM/s1600/gchisto.png" imageanchor="1" style=""&gt;&lt;img border="0" height="178" width="400" src="http://1.bp.blogspot.com/-TWREIARHndQ/UQrlnkuuTpI/AAAAAAAAAL0/JKXacJy-_EM/s400/gchisto.png" /&gt;&lt;/a&gt;
&lt;h3&gt;Plotting GC Pause Time Distribution as a Boxplot&lt;/h3&gt;
&lt;p&gt;Alternatively, you can view this as a boxplot:&lt;/p&gt;
&lt;pre class="brush: r"&gt;
plot.title &lt;- "GC Pause Time Distribution as Boxplot"
with(data.gc, {
 boxplot(pause.time ~ gc.type,main=plot.title)
}) 
&lt;/pre&gt;
&lt;a href="http://1.bp.blogspot.com/-MzjUXWGulcw/UQrlvYhjEdI/AAAAAAAAAMA/8XWrYWluwns/s1600/boxplot.png" imageanchor="1" style=""&gt;&lt;img border="0" height="178" width="400" src="http://1.bp.blogspot.com/-MzjUXWGulcw/UQrlvYhjEdI/AAAAAAAAAMA/8XWrYWluwns/s400/boxplot.png" /&gt;&lt;/a&gt;

&lt;h3&gt;Plotting Promoted Heap Memory&lt;/h3&gt;
&lt;p&gt;One particular case, I was analyzing a situation where
minor GC pause time was unusually large.  My original hypothesis is that
a lot of memory was being promoted into old generation space.  Therefore,
I wanted a plot of heap memory promoted into old gen space:&lt;/p&gt;
&lt;pre class="brush: r"&gt;
plot.title &amp;lt;- "Heap Promoted into Old Gen Space"
with(data.gc, {
 old.end &amp;lt;- kb.to.mb(heap.end - young.end)
 x &amp;lt;- sec.to.hours(timestamp)
 # If there is a full GC, promoted heap size would go negative
 y &amp;lt;- diff(old.end)
 plot(x[-1],y,
   xlab="Elapsed Time (hrs)",
   ylab="Promoted Heap (MB)",type='h',
   main=plot.title)
 grid()
})

&lt;/pre&gt;
&lt;p&gt;In this particular case, the promoted heap was not all that large as shown in the diagram below:&lt;/p&gt;

&lt;a href="http://2.bp.blogspot.com/-QfPgsqbf__k/UQrl1bZg8oI/AAAAAAAAAMM/z844ZqnHJAI/s1600/promotedheap.png" imageanchor="1" style=""&gt;&lt;img border="0" height="178" width="400" src="http://2.bp.blogspot.com/-QfPgsqbf__k/UQrl1bZg8oI/AAAAAAAAAMM/z844ZqnHJAI/s400/promotedheap.png" /&gt;&lt;/a&gt;

&lt;h3&gt;Plotting Young Gen Survived Heap&lt;/h3&gt;
&lt;p&gt;I then investigated survivor heap spaces. Here's the script that plots the survivor heap space along with GC pause time on the same chart:&lt;/p&gt;
&lt;pre class="brush: r"&gt;
# Plot survived Heap
xtitle &amp;lt;- 'Elapsed Time (hrs)'
ytitle &amp;lt;-'Survivor Heap (MB)'
plot.title &amp;lt;- "Survived Heap after Minor GC"

with(data.gc, {
   
 x &amp;lt;- sec.to.hours(timestamp)
 y &amp;lt;- kb.to.mb(young.end)
 plot(x,y,
  xlab=xtitle,ylab=ytitle,
  main=plot.title,type='o',col='blue')
  
 # Plot on secondary axis
 par(new=TRUE)
 plot(x,pause.time,
   axes=FALSE,type='o',
   col="green",pch=23,xlab="",ylab="")
 axis(4)
 mtext("GC Pause Time (sec)",4)
})
&lt;/pre&gt;

&lt;a href="http://4.bp.blogspot.com/-142oWvWQ7DU/UQrl9p892XI/AAAAAAAAAMY/7R5Y8QKfTpc/s1600/survivorheap.png" imageanchor="1" style=""&gt;&lt;img border="0" height="178" width="400" src="http://4.bp.blogspot.com/-142oWvWQ7DU/UQrl9p892XI/AAAAAAAAAMY/7R5Y8QKfTpc/s400/survivorheap.png" /&gt;&lt;/a&gt;


&lt;p&gt;In addition, I can also check the correlation coefficient via :&lt;/p&gt;
&lt;pre class="brush: r"&gt;
&gt; with(data.gc,cor(young.end[gc.type == 'minor'],pause.time[gc.type == 'minor']))
[1] 0.9659936
&lt;/pre&gt;
&lt;p&gt;As we can see from the result, the correlation was pretty high that size of
the survivor heap and its effect on GC pause time.&lt;/p&gt;
&lt;h3&gt;Checking for Memory Leaks&lt;/h3&gt;
&lt;p&gt;Another plot that I often have to generate is used as a quick check for memory leaks.
Here's the R script that plots old gen heap space usage after every full GC:&lt;/p&gt;
&lt;pre class="brush: r"&gt;
# Check for memory leak
xtitle &amp;lt;- 'Elapsed Time (hrs)'
ytitle &amp;lt;-'Old Gen Heap (MB)'


with(data.gc, {
 plot.title &amp;lt;- "Old Gen Space after Full GC"
 x &amp;lt;- sec.to.hours(timestamp[gc.type=="full"])
 y &amp;lt;- kb.to.mb(old.end[gc.type=="full"])
 plot(x,y,
   xlab=xtitle,ylab=ytitle,main=plot.title,type='o',col='blue')

 # Add fitted line
 abline(lm(y ~ x),col="red")
 retval &amp;lt;&amp;lt;- lm(y ~ x)
})
retval
&lt;/pre&gt;
&lt;p&gt;With the trend analysis line, we can potentially project when the system will run out of memory.
For the following plot, checking the linear model fit along with eyeballing the chart suggests that we don't really have a memory leak issue:&lt;/p&gt;
&lt;pre class="brush: plain"&gt;
Call:
lm(formula = y ~ x)

Coefficients:
(Intercept)            x  
   1285.978        0.298  
&lt;/pre&gt;

&lt;a href="http://1.bp.blogspot.com/-LddxQjtyhXk/UQrmGlieriI/AAAAAAAAAMk/uBWfzyB43gw/s1600/memoryleak.png" imageanchor="1" style=""&gt;&lt;img border="0" height="178" width="400" src="http://1.bp.blogspot.com/-LddxQjtyhXk/UQrmGlieriI/AAAAAAAAAMk/uBWfzyB43gw/s400/memoryleak.png" /&gt;&lt;/a&gt;

&lt;p&gt;I am delighted by the power afforded by R to perform GC analysis.  It has now become my preferred tool to perform GC analysis.  In addition, because I learned how to use R, I'm beginning to use R in other capacity, including inventory , trending, and  capacity planning analysis.
&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/uE8icfbqgrM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/7535703335006582839/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=7535703335006582839" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/7535703335006582839?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/7535703335006582839?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/uE8icfbqgrM/java-garbage-collection-log-analysis.html" title="Java Garbage Collection Log Analysis with R" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-oTXTf2rVJ-Y/UQrlfssJjkI/AAAAAAAAALo/Ibr7e-nfZVM/s72-c/gctimeline.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2013/02/java-garbage-collection-log-analysis.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkcESHY9eip7ImA9WhNaE0o.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-9137106669177405124</id><published>2013-01-28T05:00:00.000-08:00</published><updated>2013-01-28T05:00:09.862-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-01-28T05:00:09.862-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="functional programming" /><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="Tibco" /><category scheme="http://www.blogger.com/atom/ns#" term="Clojure" /><category scheme="http://www.blogger.com/atom/ns#" term="Tibco EMS" /><title>Checking Tibco EMS Queue Connected Users with Clojure</title><content type="html">&lt;p&gt;During our Tibco EMS infrastructure upgrade project, we had some rogue connections that just refused to disconnect
from the old infrastructure and switch to the new Tibco EMS infrastructure.  In order to track down these connections
and evict those connections, I had to generate a report which would tell me which user, from which host is connected 
to particular queues in Tibco EMS. Here's the script that I wrote in Clojure that generates that report.&lt;/p&gt;
&lt;pre  class="brush: clojure;"&gt;
; Get destination users (and which host the user is connecting from) along with which queue/topic it is connected to.
(defn get-destination-users [server-url username password]
  (with-open [admin (TibjmsAdmin. server-url username password)]
    (let [connections  (.getConnections admin) 
          consumers    (.getConsumers admin)
          dest-userids (-&amp;gt;&amp;gt; consumers
                         (map (fn [c] {:userid (.getConnectionID c) 
                                       :dest (.getDestinationName c)})))
          get-user (fn [dest-name id]
                     (let [conn (first (filter #(= id (.getID %)) connections))]
                       (if (nil? conn)
                         {:dest dest-name :user (str "Unknown user : " id) :host "unknown"}
                         {:dest dest-name :user (.getUserName conn) :host (.getHost conn)})))]
      (map (fn [x] (get-user (:dest x) (:userid x))) dest-userids))))

; Dump results in CSV format
(def results (get-destination-users server-url username password))   
(doseq [r (sort-by :user (set results))]
  (println  (str (:user r) "," (:host r) "," (:dest r))))

&lt;/pre&gt;&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/RjbqlexraO0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/9137106669177405124/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=9137106669177405124" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/9137106669177405124?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/9137106669177405124?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/RjbqlexraO0/checking-tibco-ems-queue-connected.html" title="Checking Tibco EMS Queue Connected Users with Clojure" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2013/01/checking-tibco-ems-queue-connected.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Dk8FQHo6eip7ImA9WhNbEUs.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-4136649713695902291</id><published>2013-01-14T04:00:00.000-08:00</published><updated>2013-01-14T04:00:11.412-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-01-14T04:00:11.412-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term=".NET" /><category scheme="http://www.blogger.com/atom/ns#" term="fsharp" /><category scheme="http://www.blogger.com/atom/ns#" term="f#" /><category scheme="http://www.blogger.com/atom/ns#" term="Clojure" /><title>Who's Connecting to My Servers?</title><content type="html">&lt;p&gt;I have been reading through the book
&lt;a href="http://www.amazon.com/exec/obidos/ASIN/1449394701/techie2biz-20"&gt;Clojure Programming&lt;/a&gt;.
In the course of reading this book, I've looked for all sorts of opportunities to try applying Clojure 
at work.  Most of the time, I've used Clojure to implement convenience scripts to 
help with my day job.  I would typically use Clojure whenever I have to work with Java based
platforms and F# for .NET based platforms.  Occasionally, I would develop scripts that does not have
any dependency and I could choose any language to implement.  What would typically happen 
is that I would choose the programming language that I used last.  
This strategy, unfortunately, would typically end up biasing me 
toward one programming language and lately, it has been biasing
me toward Clojure. 

After noticing this trend, I have decided to deliberately and consciously choose to implement
in the less frequently used language so I don't become completely rusty in the 
other programming languages.
&lt;/p&gt;

&lt;p&gt;Recently, I had to opportunity to write a small script. I was managing an infrastructure upgrade and needed to know the downstream impact.  It was an infrastructure component that that a lot of persistent inbound connections, but unfortunately, the inbound connections were neither monitored nor documented.   One way to check the connections is ask the the network engineers to setup monitoring
on the servers and collect the information on the incoming connections.  Our network
engineers are generally pretty busy and we hate to add to their existing workloads.  However,
we can effectively do the same thing by running &lt;code&gt;netstat -an&lt;/code&gt; on each 
of the target servers and taking that output dump and parse that for incoming connections.
We would do this over a period of time to try to capture most of the client connections.
&lt;/p&gt;
&lt;p&gt;The following Clojure script loads all the &lt;code&gt;netstat&lt;/code&gt; dump output files
and generate a list of all the hosts that are connected to the target servers:&lt;/p&gt;
&lt;pre  class="brush: clojure;"&gt;
(import '(java.net InetAddress))
(use '[clojure.string :only (join)])
(use '[clojure.java.io :as io])

; Load all the data from all *.data files in c:\work\servers folder
(def data (-&amp;gt;&amp;gt; "c:\\work\\servers"
   (io/file)
   (file-seq)
   (map #(.getAbsolutePath %))
   (filter #(re-matches #".*\.data$" %))
   (map #(slurp %))
   (join " ")))

; Find all ip addresses in the netstat dump
; Perform hostname lookup, discard duplicates, sort the hostnames   
(def hosts (-&amp;gt;&amp;gt; data
   (re-seq #"(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\.\d+")
   (map #(second %))
   (set)
   (map #(.getCanonicalHostName (InetAddress/getByName %)))
   (sort)
   (join "\n")))

; Dump output to clients.out file
(spit "c:\\work\\servers\\results.out" hosts)
&lt;/pre&gt;
&lt;p&gt;The above script runs with the assumption that all data fits into memory.  However, if that becomes a
problem, it is fairly trivial to sequentially read and process &lt;code&gt;netstat&lt;/code&gt; dump one file at a time
and combine the results to write to the output.&lt;/p&gt;
&lt;p&gt;The F# version is similar to Clojure version.  Grabbing the files from the folder is easier but the need to
explicitly handle exceptions adds back the additional lines of code to be about on par with code verbosity of the Clojure version.
&lt;/p&gt; 
&lt;pre  class="brush: fsharp;"&gt;
open System.IO
open System.Net
open System.Text.RegularExpressions

// Load all the data from all *.data files in c:\work\servers folder
let data = Directory.GetFiles(@"c:\work\servers","*.data")
           |&amp;gt; Seq.map File.ReadAllText
           |&amp;gt; String.concat " "

// Return hostname if it can be resolved
// otherwise return the ip address
let getHostEntry (ipaddress:string) =
    try
        Dns.GetHostEntry(ipaddress).HostName
    with
      | err -&amp;gt; ipaddress

// Find all ip addresses in the netstat dump
// Perform hostname lookup, discard duplicates, sort the hostnames
let hosts = Regex.Matches(data,@"(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\.\d+")
            |&amp;gt; Seq.cast&amp;lt;Match&amp;gt;
            |&amp;gt; Seq.map (fun m -&amp;gt; m.Groups.[1].Value)
            |&amp;gt; Set.ofSeq
            |&amp;gt; Seq.map getHostEntry
            |&amp;gt; Seq.sort
            |&amp;gt; String.concat "\n"

File.WriteAllText(@"c:\work\servers\results.out",hosts)
&lt;/pre&gt;&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/V4KdBP97u3A" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/4136649713695902291/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=4136649713695902291" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/4136649713695902291?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/4136649713695902291?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/V4KdBP97u3A/whos-connecting-to-my-servers.html" title="Who's Connecting to My Servers?" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2013/01/whos-connecting-to-my-servers.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0EERHc7cCp7ImA9WhVaFEw.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-8729539007991429678</id><published>2012-06-11T06:00:00.000-07:00</published><updated>2012-06-11T06:00:05.908-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-06-11T06:00:05.908-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="functional programming" /><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="GC" /><category scheme="http://www.blogger.com/atom/ns#" term="memory" /><category scheme="http://www.blogger.com/atom/ns#" term="Clojure" /><category scheme="http://www.blogger.com/atom/ns#" term="heap" /><title>Quick and easy way to monitor for memory leaks in Java application servers</title><content type="html">&lt;p&gt;
I was part of a postmortem analysis team that performed a root cause analysis
on why a particular node in a HA paired system failed and needed to provide recommendations to 
prevent future occurrences.  Initial examination of the log showed that the node that failed had a permgen
out of memory problem.  Management team also wanted to know if there were memory leaks
with the application.&lt;/p&gt;  
&lt;p&gt;
Unfortunately, the Java application server was not configured with GC logging nor configured to 
perform a heap dump on out of memory error condition.  Without the gc log data or the heap dump, it becomes harder to be able to explain why did the application server run out of permgen space at the time it failed.  In the interest of collecting data and to prevent future node failures, we recommended that permgen space be increased and add the following startup parameters to the Java application server&lt;/p&gt;
&lt;pre&gt;
    -XX:+PrintGCTimeStamps
    -XX:+PrintGCDetails
    -XX:+HeapDumpOnOutOfMemoryError
    -XX:HeapDumpPath=/myapp/heapdumps
    -Xloggc:/myapp/logs/gc.log
&lt;/pre&gt;
&lt;p&gt;One problem that arose during the writeup of the analysis was 
how to answer the question whether the application
had any memory leak issue.  The application team could not reproduce the problem in 
non-production environment but wanted some assurance that the application did not have
a memory leak problem with PermGen space or anywhere else in the heap.  I proposed that we
track heap usage after every Full GC over a period of time and see the heap usage trends over time.  
The heap usage trends should provide a good indication if the application
has a problematic memory leak issue or not. &lt;/p&gt;
&lt;p&gt;In order to do so, I wrote a small script to parse out only the heap and permgen space
info after a full GC from the gc log.  I wrote this script in Clojure as follows:&lt;/p&gt;
&lt;pre class="brush: clojure;"&gt;
; Define the regex pattern to parse each line in gc log
(defn full-gc-pattern []
    (let [timestamp  "([\\d\\.]+): .* " 
          space      "(\\d+)K-&gt;(\\d+)K\\((\\d+)K.+ "
          new-gen    space
          old-gen    space
          perm-gen   space
          heap       "(\\d+)K-&gt;(\\d+)K\\((\\d+)K\\)\\], "
          exec-stat  "(\\d+\\.\\d+).*" ]
         (re-pattern (str timestamp new-gen old-gen perm-gen heap exec-stat))))                 

; We only want to dump timestamp, total heap and perm gen space
; after each full GC
; Variable definitions:
;     ts - timestamp (in seconds)
;     ys - YoungGen space starting heap size (in KB)
;     ye - YoungGen space ending heap size (in KB)
;     ym - YoungGen space max heap size (in KB)
;     os - OldGen space starting heap size (in KB)
;     oe - OldGen space ending heap size (in KB)
;     om - OldGen space max heap size (in KB)
;     hs - Total heap space starting heap size (in KB)
;     he - Total heap space ending heap size (in KB)
;     hm - Total heap space max heap size (in KB)
;     ps - PermGen space starting heap size (in KB)
;     pe - PermGen space ending heap size (in KB)
;     pm - PermGen space max heap size (in KB)
(defn process-full-gc [entry]
    (let [parsed (first (re-seq (full-gc-pattern) entry))
         [a ts ys ye ym os oe om hs he hm ps pe pm &amp; e] parsed]
        (println (format "%s,%s,%s" ts he pe))))

; Load the gc log data
(def gcdata (line-seq (clojure.java.io/reader (clojure.java.io/file "gc.log"))))
        
; Process each full GC entry after filtering out minor GC entries          
(doseq [line (keep #(re-find #".*Full GC.*" %) gcdata)]
    (process-full-gc line))

&lt;/pre&gt;
&lt;p&gt;Using this script against the gc log file, and taking that output and loading into a spreadsheet,
I can plot and generate a trendline to see if memory is growing or not. Here's the heap usage plot over
approximately a month. 
&lt;/p&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://4.bp.blogspot.com/-mwviKNzA0-8/T8aOINBnULI/AAAAAAAAALU/SL4Vg-56VI8/s1600/heaptrends.PNG" imageanchor="1" style=""&gt;&lt;img border="0" height="274" width="400" src="http://4.bp.blogspot.com/-mwviKNzA0-8/T8aOINBnULI/AAAAAAAAALU/SL4Vg-56VI8/s400/heaptrends.PNG" /&gt;&lt;/a&gt;&lt;/div&gt;


&lt;p&gt;
As seen on the chart, the slope of the trendlines are small and negative, which is a good indicator
that we do not have a memory leak problem with this application.
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/SZcOcKTcKVo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/8729539007991429678/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=8729539007991429678" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/8729539007991429678?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/8729539007991429678?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/SZcOcKTcKVo/quick-and-easy-way-to-monitor-for.html" title="Quick and easy way to monitor for memory leaks in Java application servers" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-mwviKNzA0-8/T8aOINBnULI/AAAAAAAAALU/SL4Vg-56VI8/s72-c/heaptrends.PNG" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2012/06/quick-and-easy-way-to-monitor-for.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkcERnw-fyp7ImA9WhVbEkw.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-5513919273645486029</id><published>2012-05-28T06:00:00.000-07:00</published><updated>2012-05-28T06:00:07.257-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-28T06:00:07.257-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="multilingual" /><category scheme="http://www.blogger.com/atom/ns#" term="encoding" /><title>Character Encoding Troubles</title><content type="html">&lt;p&gt;In the past, I encountered an issue with a legacy application that migrated from WebLogic Server running on a old Windows server to a newer Tomcat application server running on Red Hat Linux environment.  This application has been running fine without issues for weeks until the developer suddenly started to complain that this application is not saving the registered &amp;reg; and copyright &amp;copy; trademark symbols to the database.  The developer also said that when he runs the application from his Windows laptop, he's able to save these two symbols into the database. &lt;/p&gt;
&lt;p&gt;Earlier in my career, I was developer for a software company that built multilingual software specializing in Asian languages, I recognize this as a character encoding issue.  So I asked the developer to send me the source code related to this issue and here's the relevant part of the code:&lt;/p&gt;

&lt;pre class="brush: java;"&gt;
    // Sending updates to the database
    update.setValue("data",encodeString(text));
    // Inserting the data to the database
    insert.setValue("data",encodeString(text));
&lt;/pre&gt;
&lt;p&gt;That seemed odd, what does the method &lt;code&gt;encodeString&lt;/code&gt; do?  Here's the implementation:&lt;/p&gt;
&lt;pre class="brush: java;"&gt;
public String encodeString(String value) throws java.io.UnsupportedEncodingException
{
  log("encodeString", "Begin");
  if (value == null)
  {
    log("encodeString", "value == null");
    return value;
  }
  
  byte [] btValue = value.getBytes();
  String encodedValue = new String(btValue, _ISO88591);
  
  /*
  Charset utf8charset = Charset.forName("UTF-8");
  Charset iso88591charset = Charset.forName("ISO-8859-1");

  ByteBuffer inputBuffer = ByteBuffer.wrap(btValue);

  // decode UTF-8
  CharBuffer data = utf8charset.decode(inputBuffer);

  // encode ISO-8559-1
  ByteBuffer outputBuffer = iso88591charset.encode(data);
  byte[] outputData = outputBuffer.array();
  byte[] inputData = inputBuffer.array();
 
  log("ISO-8859-1: ", new String(outputData));
  log("UTF-8: ", new String(inputData));
  
  //String encodedValue = new String(btValue, _ISO88591);
  String encodedValue = new String(inputData);
  String encodedValue_ISO88591 = new String(inputData, _ISO88591);
  //encodedValue.getBytes("UTF-8");
  log("Encoded UTF: ", encodedValue);
  log("Encoded ISO88591: ", encodedValue_ISO88591);
  */
  
  return encodedValue;
}
&lt;/pre&gt;
&lt;p&gt;Wow!  I can see that the developer is trying to get a handle on this encoding business and hence all the commented out R&amp;amp;D code, but clearly this developer is not familiar with character set encoding issues.&lt;/p&gt;
&lt;p&gt;The main problem is with the following line of code:&lt;/p&gt;
&lt;pre class="brush: java;"&gt;
  byte [] btValue = value.getBytes();
&lt;/pre&gt;
&lt;p&gt;From Java API manual, the &lt;code&gt;getBytes()&lt;/code&gt; method encodes the string into a sequence of bytes using the platform's default charset.  On the old legacy Windows Server that this application was originally running on, it was probably using Windows code page 1252, which is basically ISO-8859-1 and hence the register and copyright symbols were correctly encoded.  However, on the Red Hat Linux operating system, the default encoding was ascii and therefore the register/copyright symbol got converted into question marks. &lt;/p&gt;
&lt;p&gt; Java strings are internally unicode (UTF-16) and typically the JDBC drivers will provide the appropriate conversions to and from the database. Therefore, fix to this application is simple, all one have to do is merely change the following two lines of code and get rid of the entire &lt;code&gt;encodeString()&lt;/code&gt; method:&lt;/p&gt;
&lt;pre class="brush: java;"&gt;
    // Changed from : update.setValue("data",encodeString(text));
    update.setValue("data",text);
    // Changed from : insert.setValue("data",encodeString(text));
    insert.setValue("data",text);
&lt;/pre&gt;&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/Q6rUeQwjUEY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/5513919273645486029/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=5513919273645486029" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/5513919273645486029?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/5513919273645486029?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/Q6rUeQwjUEY/character-encoding-troubles.html" title="Character Encoding Troubles" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2012/05/character-encoding-troubles.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkMFRXY7fCp7ImA9WhVUEE0.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-6415352154028906940</id><published>2012-05-14T06:00:00.000-07:00</published><updated>2012-05-14T06:00:14.804-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-14T06:00:14.804-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Tibco" /><category scheme="http://www.blogger.com/atom/ns#" term="f#" /><category scheme="http://www.blogger.com/atom/ns#" term="Clojure" /><category scheme="http://www.blogger.com/atom/ns#" term="Tibco EMS" /><title>Working with Tibco EMS Message Topics using F# and Clojure</title><content type="html">&lt;p&gt;In my 
&lt;a href=http://jyliao.blogspot.com/2012/04/f-clojure-and-message-queues-on-tibco.html"&gt;previous blog post&lt;/a&gt;, 
I showed how to connect to a Tibco EMS Queue with F# and Clojure to represent integration interoperability
from .NET and Java platforms.  Message queues are a way to implement the Request-Reply 
pattern, which is one of the many enterprise integration patterns described in the book
&lt;a href="http://www.amazon.com/exec/obidos/ASIN/0321200683/techie2biz-20"&gt;Enterprise Integration Patterns&lt;/a&gt;. 
Another basic enterprise integration pattern is the Publish-Subscribe pattern, which
can be implement via JMS topics. This blog post shows how to connect to JMS topic from .NET and Java using Tibco EMS 
as the JMS provider.
&lt;/p&gt;
&lt;p&gt;
Here is the F# version:
&lt;/p&gt;
&lt;pre class="brush: fsharp;"&gt;
#r @&amp;quot;C:\tibco\ems\6.3\bin\TIBCO.EMS.dll&amp;quot;

open System
open TIBCO.EMS

let serverUrl = &amp;quot;tcp://localhost:7222&amp;quot;
let producer = &amp;quot;producer&amp;quot;
let consumer = &amp;quot;consumer&amp;quot;
let password = &amp;quot;testpwd&amp;quot;
let topicName = &amp;quot;testTopic&amp;quot;


let subscribeToTopic serverUrl userid password topicName messageProcessor =
    async {
        let connection = (userid,password)
                         |&amp;gt; (new TopicConnectionFactory(serverUrl)).CreateTopicConnection
        let session = connection.CreateTopicSession(false,Session.AUTO_ACKNOWLEDGE)
        let topic = session.CreateTopic(topicName)
        let subscriber =  session.CreateSubscriber(topic)
        connection.Start()
        printf &amp;quot;Subscriber connected!\n&amp;quot;
        while true do
            try
                subscriber.Receive() |&amp;gt; messageProcessor
            with _ -&amp;gt;  ()
        connection.Close()
    }

let publishTopicMessages serverUrl  userid password topicName messages =
    let connection = (userid,password)
                     |&amp;gt; (new TopicConnectionFactory(serverUrl)).CreateTopicConnection
    let session = connection.CreateTopicSession(false,Session.AUTO_ACKNOWLEDGE)
    let topic = session.CreateTopic(topicName)
    let publisher = session.CreatePublisher(topic)
    connection.Start()

    messages
    |&amp;gt; Seq.iter (fun item -&amp;gt; session.CreateTextMessage(Text=item)
                             |&amp;gt; publisher.Send)
                             
    connection.Close()

// Just dump message to console for now
let myMessageProcessor (msg:Message) =
    msg.ToString() |&amp;gt; printf &amp;quot;%s\n&amp;quot;

let consumeMessageAsync = subscribeToTopic &amp;quot;tcp://localhost:7222&amp;quot; &amp;quot;consumer&amp;quot; &amp;quot;testpwd&amp;quot;


let produceMessages topicName messages = publishTopicMessages &amp;quot;tcp://localhost:7222&amp;quot; &amp;quot;producer&amp;quot; &amp;quot;testpwd&amp;quot; topicName messages 


// Asynchronously start the topic subscriber
Async.Start(consumeMessageAsync &amp;quot;testTopic&amp;quot; myMessageProcessor)


// Publish messages to the Tibco EMS topic
[ &amp;quot;Aslund&amp;quot;; &amp;quot;Barrayar&amp;quot;; &amp;quot;Beta Colony&amp;quot;; &amp;quot;Cetaganda&amp;quot;; &amp;quot;Escobar&amp;quot;; &amp;quot;Komarr&amp;quot;; &amp;quot;Marilac&amp;quot;; &amp;quot;Pol&amp;quot;; &amp;quot;Sergyar&amp;quot;; &amp;quot;Vervain&amp;quot;]
|&amp;gt; produceMessages &amp;quot;testTopic&amp;quot;


printf &amp;quot;Done!&amp;quot;

&lt;/pre&gt;
&lt;p&gt;One thing to point out is that Tibco, unfortunately, did not implement IDisposable for it's Connection objects;
perhaps in it's bid to stay faithful to the Java API.  That design choice seems unfortunate to me in the sense that I no longer
can leverage C#'s &lt;code&gt;using&lt;/code&gt; keyword or F#'s &lt;code&gt;use&lt;/code&gt; keyword to automatically close connection.
I suppose it is fairly trivial to subclass the &lt;code&gt;QueueConnection&lt;/code&gt; and &lt;code&gt;TopicConnection&lt;/code&gt; class 
and add the IDisposable interface, but I feel that
Tibco should have done this and developed the Tibco .NET API using idioms that are .NET specific.&lt;/p&gt;
&lt;p&gt;Putting my rants aside, here is the equivalent Clojure code to connect to Tibco Topics:&lt;/p&gt;
&lt;pre class="brush: clojure;"&gt;
(import &amp;#39;(java.util Enumeration)
        &amp;#39;(com.tibco.tibjms TibjmsTopicConnectionFactory)
        &amp;#39;(javax.jms Message JMSException  Session
                    Topic TopicConnectionFactory
                    TopicConnection TopicSession
                    TopicSubscriber))
                  
(def serverUrl &amp;quot;tcp://localhost:7222&amp;quot;)
(def producer &amp;quot;producer&amp;quot;)
(def consumer &amp;quot;consumer&amp;quot;)
(def password &amp;quot;testpwd&amp;quot;)
(def topicName &amp;quot;testTopic&amp;quot;)

;------------------------------------------------------------------------------
; Subscribe to Topic asynchronously
;------------------------------------------------------------------------------
(defn subscribe-topic [server-url user password topic-name process-message]
    (future
        (with-open [connection (-&amp;gt; (TibjmsTopicConnectionFactory. server-url)
                                   (.createTopicConnection user password))]
            (let [session (.createTopicSession connection false Session/AUTO_ACKNOWLEDGE)
                  topic (.createTopic session  topic-name)]
                (with-open [subscriber (.createSubscriber session topic)]
                    (.start connection)
                    (loop []                       
                        (process-message (.receive subscriber))
                        (recur)))))))

;------------------------------------------------------------------------------
; Publishing to a Topic
;------------------------------------------------------------------------------
(defn publish-to-topic [server-url user password topic-name messages]
    (with-open [connection (-&amp;gt; (TibjmsTopicConnectionFactory. server-url)
                               (.createTopicConnection user password))]
        (let [session (.createTopicSession connection false Session/AUTO_ACKNOWLEDGE)
              topic (.createTopic session  topic-name)
              publisher (.createPublisher session topic)]
            (.start connection)
            (doseq [item messages]
                (let [message (.createTextMessage session)]
                    (.setText message item)
                    (.publish publisher message))))))
                    
                      
; Create function aliases with connection information embedded                    
(defn produce-messages [topic-name messages]
    (publish-to-topic &amp;quot;tcp://localhost:7222&amp;quot; &amp;quot;producer&amp;quot; &amp;quot;testpwd&amp;quot; topic-name messages))

(defn consume-messages [topic-name message-processor]
    (subscribe-topic &amp;quot;tcp://localhost:7222&amp;quot; &amp;quot;consumer&amp;quot; &amp;quot;testpwd&amp;quot; topic-name message-processor))

; Just dump messages to console for now
(defn my-message-processor [message]
    (println (.toString message)))
    
; Start subscribing messages asynchronously
(consume-messages &amp;quot;testTopic&amp;quot; my-message-processor)                            
    
; Publish to topic
(def my-messages &amp;#39;(&amp;quot;alpha&amp;quot; &amp;quot;beta&amp;quot; &amp;quot;gamma&amp;quot; &amp;quot;delta&amp;quot;
                   &amp;quot;epsilon&amp;quot; &amp;quot;zeta&amp;quot; &amp;quot;eta&amp;quot; &amp;quot;theta&amp;quot;
                   &amp;quot;iota&amp;quot; &amp;quot;kappa&amp;quot; &amp;quot;lambda&amp;quot; &amp;quot;mu&amp;quot; &amp;quot;nu&amp;quot;
                   &amp;quot;xi&amp;quot;, &amp;quot;omicron&amp;quot; &amp;quot;pi&amp;quot; &amp;quot;rho&amp;quot;
                   &amp;quot;signma&amp;quot; &amp;quot;tau&amp;quot; &amp;quot;upsilon&amp;quot; &amp;quot;phi&amp;quot;,
                   &amp;quot;chi&amp;quot; &amp;quot;psi&amp;quot; &amp;quot;omega&amp;quot;))                    

(produce-messages  &amp;quot;testTopic&amp;quot;  my-messages)    
&lt;/pre&gt;
&lt;p&gt;When I fire up both scripts, the messages published to the topic would be received by both the .NET and Java clients.
With these scripts, I can easily swap out the message generators or message processors as needed for any future testing scenarios.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/iyXwWM0ENhw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/6415352154028906940/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=6415352154028906940" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/6415352154028906940?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/6415352154028906940?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/iyXwWM0ENhw/working-with-tibco-ems-message-topics.html" title="Working with Tibco EMS Message Topics using F# and Clojure" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2012/05/working-with-tibco-ems-message-topics.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ck8GQnkyeSp7ImA9WhVWF0Q.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-5708525842429297558</id><published>2012-04-30T06:00:00.000-07:00</published><updated>2012-04-30T06:00:23.791-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-04-30T06:00:23.791-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="Messaging" /><category scheme="http://www.blogger.com/atom/ns#" term="JMS" /><category scheme="http://www.blogger.com/atom/ns#" term=".NET" /><category scheme="http://www.blogger.com/atom/ns#" term="Tibco" /><category scheme="http://www.blogger.com/atom/ns#" term="f#" /><category scheme="http://www.blogger.com/atom/ns#" term="Clojure" /><category scheme="http://www.blogger.com/atom/ns#" term="Tibco EMS" /><title>F#, Clojure and Message Queues on Tibco EMS</title><content type="html">&lt;body&gt;
&lt;script type="text/javascript" src="https://raw.github.com/sattvik/sh-clojure/master/src/shBrushClojure.js"&gt;&lt;/script&gt;
&lt;p&gt;It looks like I will be getting much more hands on with Tibco EMS.  Since the Tibco EMS system in use will have connections from both .NET platforms and Java platforms, I wanted to write some scripts to run some engineering tests on Tibco EMS.   I decided to simulate .NET side connections with F# and Java side connections with Clojure.  Taking the sample code from Tibco installation, I created the following F# script  that sends messages to a queue from the sample C# code:
&lt;pre class="brush: fsharp;"&gt;
#r @&amp;quot;C:\tibco\ems\6.3\bin\TIBCO.EMS.dll&amp;quot;

open System
open TIBCO.EMS

let serverUrl = &amp;quot;tcp://localhost:7222&amp;quot;
let producer = &amp;quot;producer&amp;quot;
let consumer = &amp;quot;consumer&amp;quot;
let password = &amp;quot;testpwd&amp;quot;
let queueName = &amp;quot;testQueue&amp;quot;


let getQueueTextMessages serverUrl  userid password queueName messageProcessor =
    async {
        let connection = (userid,password)
                         |&amp;gt; (new QueueConnectionFactory(serverUrl)).CreateQueueConnection
        let session = connection.CreateQueueSession(false,Session.AUTO_ACKNOWLEDGE)
        let queue = session.CreateQueue(queueName)
        let receiver =  session.CreateReceiver(queue)
        connection.Start()
        printf &amp;quot;Queue connection established!&amp;quot;
        while true do
            try
                receiver.Receive() |&amp;gt; messageProcessor
            with _ -&amp;gt;  ()
    }


let sendQueueTextMessages serverUrl  userid password queueName messages =
    let connection = (userid,password)
                     |&amp;gt; (new QueueConnectionFactory(serverUrl)).CreateQueueConnection
    let session = connection.CreateQueueSession(false,Session.AUTO_ACKNOWLEDGE)
    let queue = session.CreateQueue(queueName)
    let sender = session.CreateSender(queue)
    connection.Start()

    messages
    |&amp;gt; Seq.iter (fun item -&amp;gt; session.CreateTextMessage(Text=item)
                             |&amp;gt; sender.Send)
                             
    connection.Close()



// Just dump message to console for now
let myMessageProcessor (msg:Message) =
    msg.ToString() |&amp;gt; printf &amp;quot;%s\n&amp;quot;


let consumeMessageAsync = getQueueTextMessages &amp;quot;tcp://localhost:7222&amp;quot; &amp;quot;consumer&amp;quot; &amp;quot;testpwd&amp;quot;
let produceMessages queueName messages = sendQueueTextMessages &amp;quot;tcp://localhost:7222&amp;quot; &amp;quot;producer&amp;quot; &amp;quot;testpwd&amp;quot; queueName messages 

// Start message consumer asynchronously
Async.Start(consumeMessageAsync &amp;quot;testQueue&amp;quot; myMessageProcessor)


// Send messages to the Tibco EMS   
[ &amp;quot;Aslund&amp;quot;; &amp;quot;Barrayar&amp;quot;; &amp;quot;Beta Colony&amp;quot;; &amp;quot;Cetaganda&amp;quot;; &amp;quot;Escobar&amp;quot;; &amp;quot;Komarr&amp;quot;; &amp;quot;Marilac&amp;quot;; &amp;quot;Pol&amp;quot;; &amp;quot;Sergyar&amp;quot;; &amp;quot;Vervain&amp;quot;]
|&amp;gt; produceMessages &amp;quot;testQueue&amp;quot;

&lt;/pre&gt;
&lt;p&gt;The queue consumer is implemented asynchronously so it won't block executing subsequent statements.  To test Tibco JMS from Java, here is the equivalent Clojure code:&lt;/p&gt;
&lt;pre class="brush: clojure;"&gt;
(import &amp;#39;(java.util Enumeration)
        &amp;#39;(com.tibco.tibjms TibjmsQueueConnectionFactory)
        &amp;#39;(javax.jms Message JMSException  Session
                    Queue QueueBrowser 
                    QueueConnection QueueReceiver 
                    QueueSession QueueSender))
                  
(def serverUrl &amp;quot;tcp://localhost:7222&amp;quot;)
(def producer &amp;quot;producer&amp;quot;)
(def consumer &amp;quot;consumer&amp;quot;)
(def password &amp;quot;testpwd&amp;quot;)
(def queueName &amp;quot;testQueue&amp;quot;)

; Consume Queue Text messages asynchronously
(defn get-queue-text-messages [server-url user password queue-name process-message]
    (future
        (with-open [connection (-&amp;gt; (TibjmsQueueConnectionFactory. server-url)
                                   (.createQueueConnection user password))]
            (let [session (.createQueueSession connection false Session/AUTO_ACKNOWLEDGE)
                  queue (.createQueue session  queue-name)]
                (with-open [receiver (.createReceiver session queue)]              
                    (.start connection)
                    (loop []                       
                        (process-message (.receive receiver))
                        (recur)))))))
                   
; Send multiple Text messages
(defn send-queue-text-messages [server-url user password queue-name messages]
    (with-open [connection (-&amp;gt; (TibjmsQueueConnectionFactory. server-url)
                               (.createQueueConnection user password))]
        (let [session (.createQueueSession connection false Session/AUTO_ACKNOWLEDGE)
              queue (.createQueue session  queue-name)
              sender (.createSender session queue)]
            (.start connection)
            (doseq [item messages]
                (let [message (.createTextMessage session)]
                    (.setText message item)
                    (.send sender message))))))


; Create function aliases with connection information embedded                    
(defn consume-messages [queue-name message-processor]
    (get-queue-text-messages  serverUrl producer password queue-name message-processor))

(defn produce-messages [queue-name messages]
    (send-queue-text-messages  serverUrl producer password queue-name messages))

; Just dump messages to console for now
(defn my-message-processor [message]
    (println (.toString message)))

    
; Start consuming messages asynchronously
(consume-messages &amp;quot;testQueue&amp;quot; my-message-processor)                            

; Send messages to queue
(def my-messages &amp;#39;(&amp;quot;alpha&amp;quot; &amp;quot;beta&amp;quot; &amp;quot;gamma&amp;quot; &amp;quot;delta&amp;quot;
                   &amp;quot;epsilon&amp;quot; &amp;quot;zeta&amp;quot; &amp;quot;eta&amp;quot; &amp;quot;theta&amp;quot;
                   &amp;quot;iota&amp;quot; &amp;quot;kappa&amp;quot; &amp;quot;lambda&amp;quot; &amp;quot;mu&amp;quot; &amp;quot;nu&amp;quot;
                   &amp;quot;xi&amp;quot;, &amp;quot;omicron&amp;quot; &amp;quot;pi&amp;quot; &amp;quot;rho&amp;quot;
                   &amp;quot;signma&amp;quot; &amp;quot;tau&amp;quot; &amp;quot;upsilon&amp;quot; &amp;quot;phi&amp;quot;,
                   &amp;quot;chi&amp;quot; &amp;quot;psi&amp;quot; &amp;quot;omega&amp;quot;))                    

(produce-messages  &amp;quot;testQueue&amp;quot;  my-messages)    

&lt;/pre&gt;
&lt;p&gt;With these scripts, I can easily swap in different message generators and message processors as needed for any testing purposes.
When I fired up both these scripts up to the part where queue consumers are running in both F# and Clojure version and then send the messages, I could see that Tibco EMS send half the messages to my F# script and the other half to my Clojure script.  Since both of these scripts run in REPL environment, I can easily adjust my level of testing as I get results.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/-akvEeL4fC0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/5708525842429297558/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=5708525842429297558" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/5708525842429297558?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/5708525842429297558?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/-akvEeL4fC0/f-clojure-and-message-queues-on-tibco.html" title="F#, Clojure and Message Queues on Tibco EMS" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>1</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2012/04/f-clojure-and-message-queues-on-tibco.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0UFQ30-fSp7ImA9WhVXFUU.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-3126946494111070288</id><published>2012-04-16T06:00:00.000-07:00</published><updated>2012-04-16T06:00:12.355-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-04-16T06:00:12.355-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="functional programming" /><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint 2010" /><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="f#" /><category scheme="http://www.blogger.com/atom/ns#" term="C#" /><category scheme="http://www.blogger.com/atom/ns#" term=".NET 3.5" /><category scheme="http://www.blogger.com/atom/ns#" term="Microsoft" /><title>F# and SharePoint 2010 Object Hierarchy/Properties</title><content type="html">&lt;p&gt;I had the opportunity to take a SharePoint 2010 class recently.  In the class, the labs were mostly a cut and paste affair
due to time limitations.  Those lab exercises only helps me to become familiar with working in Visual Studio and seeing some of 
the SharePoint API, but does not really help me engage actively in thinking about what I was actually doing.  After the class, I decided to 
write a F# equivalent of the lab exercises to help me get a deeper understanding and to learn.  The class did impress on me that the tooling 
for SharePoint 2010 development is so much more  superior in C# such that I've decided to build only the logic code in F# and retain the 
C# SharePoint project.   This blog post describes my effort in getting that first class exercise working with F# code.&lt;/p&gt;
&lt;p&gt;I ended up creating an empty SharePoint 2010 C# project that references a F# library project. 
I added two application pages to the SharePoint 2010 project, the first being FarmHierarchy.aspx.  
In FarmHierarchy.aspx, I added the following markup between the opening and closing tags of the 
&lt;code&gt;&amp;lt;asp:Content&amp;gt;&lt;/code&gt;
element that has an &lt;b&gt;ID&lt;/b&gt; of &lt;b&gt;Main&lt;/b&gt;:&lt;/p&gt;
&lt;pre class="brush: xml;"&gt;
&amp;lt;h2&amp;gt;My Farm&amp;lt;/h2&amp;gt;
&amp;lt;asp:TreeView ID="farmHierarchyViewer" runat="server"
    ShowLines="true" EnableViewState="true"&amp;gt;&amp;lt;/asp:TreeView&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The second application page I created was PropertyChanger.aspx.  I added the following markup between the opening and closing tags of the 
&lt;code&gt;&amp;lt;asp:Content&amp;gt;&lt;/code&gt;
elsement that has an &lt;b&gt;ID&lt;/b&gt; of &lt;b&gt;Main&lt;/b&gt;:&lt;/p&gt;
&lt;pre class="brush: xml;"&gt;
    &amp;lt;h2&amp;gt;Properties:&amp;lt;/h2&amp;gt;
    &amp;lt;asp:Label ID="objectName" runat="server" Text=""&amp;gt;&amp;lt;/asp:Label&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;
    &amp;lt;asp:Panel ID="webProperties" runat="server" Visible="false" BorderColor="Orange" BorderStyle="Dashed" BorderWidth="1"&amp;gt;
        &amp;lt;asp:Label ID="WebLabel" runat="server" Text="Web Title"&amp;gt;&amp;lt;/asp:Label&amp;gt;
        &amp;lt;br/&amp;gt;
        &amp;lt;asp:TextBox ID="webTitle" runat="server" EnableViewState="true"&amp;gt;&amp;lt;/asp:TextBox&amp;gt;
        &amp;amp;nbsp;
        &amp;lt;asp:Button ID="webTitleUpdate" runat="server" Text="Update"/&amp;gt;
        &amp;amp;nbsp;
        &amp;lt;asp:Button ID="webCancel" runat="server" Text="Cancel" /&amp;gt;
    &amp;lt;/asp:Panel&amp;gt;
    &amp;lt;asp:Panel ID="listProperties" runat="server" Visible="false" BorderColor="Orange" BorderStyle="Dashed" BorderWidth="1"&amp;gt;
        &amp;lt;asp:Label ID="ListLabel" runat="server" Text="List Properties"&amp;gt;&amp;lt;/asp:Label&amp;gt;
        &amp;lt;br/&amp;gt;
        &amp;lt;asp:CheckBox ID="listVersioning" runat="server" EnableViewState="true" Text="Enable Versioning" /&amp;gt;
        &amp;lt;br/&amp;gt;
        &amp;lt;asp:CheckBox ID="listContentTypes" runat="server" EnableViewState="true" Text="Enable Content Types" /&amp;gt;
        &amp;amp;nbsp;
        &amp;lt;asp:Button ID="listPropertiesUpdate" runat="server" Text="Update" /&amp;gt;
        &amp;amp;nbsp;
        &amp;lt;asp:Button ID="listCancel" runat="server" Text="Cancel"/&amp;gt;
    &amp;lt;/asp:Panel&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The F# code would iterate through the services, Web applications, site collections, and lists in the SharePoint farm,
with the details added to nodes in the TreeView control:&lt;/p&gt;
&lt;pre class="brush: fsharp;"&gt;

module FsLab01

open System
open System.Web.UI.WebControls
open Microsoft.SharePoint
open Microsoft.SharePoint.Administration
open System.Web.UI

// Convenience methods
let nullfunc _ = ()

// Copied from Clojure
let cond clauses =
    let (_,func) = clauses |&amp;gt; Seq.find (fun (pred,func) -&amp;gt; pred)
    func()


let navListUrl url (id:Guid) =
    sprintf "%s/_layouts/lab01/PropertyChanger.aspx?type=list&amp;amp;objectID=%s" url &amp;lt;| id.ToString()


let navWebUrl url (id:Guid) =
    sprintf "%s/_layouts/lab01/PropertyChanger.aspx?type=web&amp;amp;objectID=%s" url &amp;lt;| id.ToString()

// Recursively add SPWeb &amp; SPList objects
let rec addWeb (web:SPWeb) (parentNode:TreeNode) =

    let node = new TreeNode(web.Title,null,null,navWebUrl web.Url web.ID, "_self")
    node |&amp;gt; parentNode.ChildNodes.Add

    [0..(web.Lists.Count-1)]
    |&amp;gt; Seq.map (fun i -&amp;gt; web.Lists.[i])
    |&amp;gt; Seq.iter (fun item -&amp;gt; 
                      new TreeNode(item.Title,null,null,navListUrl web.Url item.ID,"_self")
                      |&amp;gt; node.ChildNodes.Add)
        
    [0..(web.Webs.Count-1)]
    |&amp;gt; Seq.map (fun i -&amp;gt; web.Webs.[i])
    |&amp;gt; Seq.iter (fun item -&amp;gt; try addWeb item node finally item.Dispose())


// Main function to be called by FarmHierarchy.aspx code to build TreeView control
let loadViewer (viewer:TreeView) (farm:SPFarm) =

    let processSite (webappnode:TreeNode) (site:SPSite) =
        site.CatchAccessDeniedException &amp;lt;- false
        try
            let node = new TreeNode(Text=site.Url)
            node |&amp;gt; webappnode.ChildNodes.Add

            addWeb site.RootWeb node

        finally
            site.CatchAccessDeniedException &amp;lt;- false
            

    let processWebApp (svcNode:TreeNode) (webapp:SPWebApplication) = 
        let node = new TreeNode(Text=webapp.DisplayName )
        node |&amp;gt; svcNode.ChildNodes.Add

        cond [(not webapp.IsAdministrationWebApplication,
               fun _ -&amp;gt; webapp.Sites |&amp;gt; Seq.iter (processSite node));
              (true,nullfunc)]
            
                

    let processService (svc:SPService) =
        let label = sprintf "FarmService (Type=%s; Status=%s)" (svc.TypeName) (svc.Status.ToString())
        let node = new TreeNode(Text=label)
        node |&amp;gt; viewer.Nodes.Add
        match svc with
        | :? SPWebService as websvc -&amp;gt; websvc.WebApplications |&amp;gt; Seq.iter (processWebApp node)
        | _ -&amp;gt; ()

    
    viewer.Nodes.Clear()
    farm.Services |&amp;gt; Seq.iter processService
    viewer.ExpandAll()

&lt;/pre&gt;
&lt;p&gt;As I was creating &lt;code&gt;loadViewer&lt;/code&gt; method, I was bothered by the if-else-then clauses in the code.  
I had a previous &lt;a href="http://jyliao.blogspot.com/2012/01/translating-if-then-else-control-flow.html"&gt;blog&lt;/a&gt; that talked about this issue when it struck me that what I really wanted 
was something similar to the &lt;code&gt;cond&lt;/code&gt; macro in Clojure. Hence the convenience function called &lt;code&gt;cond&lt;/code&gt;
in the above F# code.&lt;/p&gt;
&lt;p&gt;With the F# code written and packaged as a library,  I can now use the above F# function in FarmHierarchy.aspx as follows:&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;
        protected void Page_Load(object sender, EventArgs e)
        {
            SPFarm thisFarm = SPFarm.Local;
            FsLab01.loadViewer(farmHierarchyViewer, thisFarm);
        }

&lt;/pre&gt;
&lt;p&gt;The second part of this lab was to create code to manipulate properties of SharePoint &lt;code&gt;SPWeb&lt;/code&gt; or &lt;code&gt;SPList&lt;/code&gt;
objects.  The F# code that manipulates the SharePoint object properties and the UI are as follow:&lt;/p&gt;
&lt;pre class="brush: fsharp;"&gt;


let changeProperty (page:Page) (objectName:Label) (webtitle:TextBox) 
                   (listpanel:Panel) (webpanel:Panel) 
                   (listVersioning:CheckBox) (listContentTypes:CheckBox) 
                   (webUpdateBtn:Button) (listUpdateBtn:Button)
                   (webCancelBtn:Button) (listCancelBtn:Button)   =    


    let homeurl  baseurl = sprintf "%s/_layouts/lab01/FarmHierarchy.aspx" baseurl

    let wrapupdates (web:SPWeb) action =
        web.AllowUnsafeUpdates &amp;lt;- true
        action()
        web.AllowUnsafeUpdates &amp;lt;- false
        

    let hidepanels () =
        listpanel.Visible &amp;lt;- false
        webpanel.Visible &amp;lt;- false

    let cancel (web:SPWeb) =
        page.Response.Redirect(homeurl web.Url)
    
        
    let checkNull item = 
        cond [(item=null,  fun _ -&amp;gt; objectName.Text &amp;lt;- "Malformed URL"
                                    hidepanels()
                                    "");
              (item&amp;lt;&amp;gt;null, fun _ -&amp;gt; item.ToString())]


    try
        let objectType = checkNull page.Request.["type"]
        let objectId = checkNull page.Request.["objectID"]

        match objectType with
        | "web" -&amp;gt; 
            listpanel.Visible &amp;lt;- false
            webpanel.Visible &amp;lt;- true
            use web = SPContext.Current.Site.OpenWeb(new Guid(objectId))

            // Hook up the events
            let myupdates _ =
                web.Title &amp;lt;- webtitle.Text
                web.Update()

            webUpdateBtn.Click.Add(
                fun _ -&amp;gt;
                    try 
                        wrapupdates web myupdates

                        page.Response.Redirect(homeurl web.Url)
                    with ex -&amp;gt;
                        objectName.Text &amp;lt;- ex.Message
                        hidepanels()
                )

            webCancelBtn.Click.Add(fun _ -&amp;gt; cancel web)

            objectName.Text &amp;lt;- sprintf "Web: %s" web.Title

            cond [(not page.IsPostBack,
                   fun () -&amp;gt; webtitle.Text &amp;lt;- web.Title);
                   (true,nullfunc)]

        | "list" -&amp;gt; 
            listpanel.Visible &amp;lt;- true
            webpanel.Visible &amp;lt;- false
            let web = SPContext.Current.Web
            let splist = web.Lists.[new Guid(objectId)]


            let myupdates _ =
                splist.EnableVersioning &amp;lt;- listVersioning.Checked
                splist.ContentTypesEnabled &amp;lt;- listContentTypes.Checked
                splist.Update()

            listUpdateBtn.Click.Add(
                fun _ -&amp;gt;
                    try 
                        wrapupdates web myupdates
                        page.Response.Redirect(homeurl web.Url)
                    with ex -&amp;gt;
                        objectName.Text &amp;lt;- ex.Message
                        hidepanels())

            listCancelBtn.Click.Add(fun _ -&amp;gt; cancel web)

            cond [(not page.IsPostBack,
                   fun _ -&amp;gt; listVersioning.Checked   &amp;lt;- splist.EnableVersioning
                            listContentTypes.Checked &amp;lt;- splist.ContentTypesEnabled)]
        | _ -&amp;gt; ()


    with  ex-&amp;gt; objectName.Text &amp;lt;- ex.Message
&lt;/pre&gt;
&lt;p&gt;I needed to sign my F# code in order it to work in SharePoint.  Since the C# SharePoint 2010 already created the key, all I had to do is to 
add &lt;code&gt;--keyfile:&amp;lt;Location to keyfile&amp;gt;\key.snk&lt;/code&gt; to the F# build setting's "Other Flags" properties.
In addition, before I can build and deploy, I needed to add the F# library dll to the C# SharePoint project's package.
I can do so by clicking on Package in the SharePoint project and then click on the Advanced button.&lt;/p&gt;
&lt;p&gt;I can now call this F# function from PropertyChanger.aspx code as follows:&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;
            FsLab01.changeProperty(this.Page, objectName, webTitle,
                                   listProperties, webProperties, 
                                   listVersioning, listContentTypes,
                                   webTitleUpdate, listPropertiesUpdate,
                                   webCancel,listCancel);
&lt;/pre&gt;
&lt;p&gt;Here's how what FarmHierarchy.aspx would look like :&lt;/p&gt;
&lt;div class="separator" style="clear: both; text-align: left;"&gt;
&lt;a href="http://1.bp.blogspot.com/-Y9LYfjtdyMM/T2vEicb2seI/AAAAAAAAAKs/PdRlp2_ct2s/s1600/farmhierarchy.png" imageanchor="1" style=""&gt;&lt;img border="0" height="228" width="400" src="http://1.bp.blogspot.com/-Y9LYfjtdyMM/T2vEicb2seI/AAAAAAAAAKs/PdRlp2_ct2s/s400/farmhierarchy.png" /&gt;&lt;/a&gt;&lt;/div&gt;


&lt;p&gt;Here's how what PropertyChanger.aspx would look like if I wanted to modify a SPWeb object:&lt;/p&gt;
&lt;div class="separator" style="clear: both; text-align: left;"&gt;
&lt;a href="http://4.bp.blogspot.com/-idJlhaNlDb4/T2vE1-vHyZI/AAAAAAAAAK4/kt1nW5ooBvo/s1600/ModifySPWeb.png" imageanchor="1" style=""&gt;&lt;img border="0" height="226" width="400" src="http://4.bp.blogspot.com/-idJlhaNlDb4/T2vE1-vHyZI/AAAAAAAAAK4/kt1nW5ooBvo/s400/ModifySPWeb.png" /&gt;&lt;/a&gt;&lt;/div&gt;


&lt;p&gt;Here's how what PropertyChanger.aspx would look like if I wanted to modify a SPList object:&lt;/p&gt;
&lt;div class="separator" style="clear: both; text-align: left;"&gt;
&lt;a href="http://2.bp.blogspot.com/-JzfBVhyizng/T2vE-aj_GYI/AAAAAAAAALE/WeKrdnT6exY/s1600/ModifySPList.png" imageanchor="1" style=""&gt;&lt;img border="0" height="229" width="400" src="http://2.bp.blogspot.com/-JzfBVhyizng/T2vE-aj_GYI/AAAAAAAAALE/WeKrdnT6exY/s400/ModifySPList.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/yVOdaf2uSy8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/3126946494111070288/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=3126946494111070288" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/3126946494111070288?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/3126946494111070288?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/yVOdaf2uSy8/f-and-sharepoint-2010-object.html" title="F# and SharePoint 2010 Object Hierarchy/Properties" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-Y9LYfjtdyMM/T2vEicb2seI/AAAAAAAAAKs/PdRlp2_ct2s/s72-c/farmhierarchy.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2012/04/f-and-sharepoint-2010-object.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE8EQ3s-fyp7ImA9WhVRF0s.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-2386006987430336213</id><published>2012-03-26T06:00:00.000-07:00</published><updated>2012-03-26T06:00:02.557-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-03-26T06:00:02.557-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".NET" /><category scheme="http://www.blogger.com/atom/ns#" term="Windows Server AppFabric" /><category scheme="http://www.blogger.com/atom/ns#" term="AppFabric" /><category scheme="http://www.blogger.com/atom/ns#" term="f#" /><category scheme="http://www.blogger.com/atom/ns#" term="PowerShell" /><title>F# and Windows Server AppFabric Cache</title><content type="html">&lt;p&gt;I recently started investigating Windows Server AppFabric, which is different from Windows Azure AppFabric, and trying understand how to use AppFabric, what are some of the operational support implications and functional capabilities and limitations.  To help me get going, I have been reading the book &lt;a href="http://www.amazon.com/gp/product/1430228172?tag=techie2biz-20"&gt;Pro Windows Server AppFabric&lt;/a&gt; by Stephen Kaufman and going through the &lt;a href="http://www.microsoft.com/download/en/details.aspx?id=7956"&gt; Windows Server AppFabric Training Kit&lt;/a&gt;.  The free downloadable training kit was more valuable in helping me understand Windows Server AppFabric.&lt;/p&gt;
&lt;p&gt;As always, the only way to truly learn new technology is to take it out for a spin.  Here are some of the powershell scripts that I've used to create the cache and get it going:&lt;/p&gt;
&lt;pre class="brush: powershell;"&gt;
# Import the necessary administration modules
Import-Module DistributedCacheAdministration
Import-Module DistributedCacheConfiguration

# List the available powershell commands
Get-Command -module DistributedCacheAdministration   
Get-Command -module DistributedCacheConfiguration


# These were run on the localhost of AppFabric
Use-CacheCluster

# Start the cache cluster
Start-CacheCluster

# Check to make sure it is up and running
Get-CacheHost

# Create a new cache 
New-Cache -CacheName MyTestCache -TimeToLive 60 -Expirable true

# Check cache is created
Get-Cache

# Grant local user access to cache
Grant-CacheAllowedClientAccount MyUserId

# Check security settings
Get-CacheAllowedClientAccounts

# After a few runs, check Cache statistics
Get-CacheStatistics -CacheName MyTestCache 

# Get Cache configuration information
Get-CacheConfig MyTestCache 


# Creating a Cache with HA - all server needs to be on 
# Windows Server Enterprise Edition
# Any host not on Enterprise Edition will not start cache cluster
New-Cache MyNewHACache -Secondaries 1

# Modifying the cache to enable callbacks
Set-CacheConfig -CacheName MyTestCache -NotificationsEnabled true -TimeToLive 180

&lt;/pre&gt;
&lt;p&gt;For the client, I wrote some F# scripts to experiment with Windows Server AppFabric.  I ran these on the local server and the loopback adapter enabled:&lt;/p&gt;
&lt;pre class="brush: fsharp;"&gt;
// I copied the libraries from c:\windows\system32\appfabric
#r @"C:\lib\Microsoft.ApplicationServer.Caching.Client.dll"
#r @"C:\lib\Microsoft.ApplicationServer.Caching.Core.dll"

open System
open System.Collections.Generic
open Microsoft.ApplicationServer.Caching

//  Make sure you grant
// grant-cacheallowedclientaccount my-user-id

// Expensive operation, do this once on startup
let dcf = new DataCacheFactory(
            let servers = new List&amp;lt;DataCacheServerEndpoint&amp;gt;()
            new DataCacheServerEndpoint("localhost",22233) |&amp;gt; servers.Add
            new DataCacheFactoryConfiguration(Servers=servers))
printfn "Data Cache Factory Created!"

// Testing add/get in default region
let cache = dcf.GetCache("MyTestCache")
let retval = cache.Add("mykey","hello app fabric!") 
cache.Get("mykey")

// Create a new region in the cache, this will pin to a single cache node
cache.CreateRegion("stocks")

// Helper function to create DataCacheTags
let createTags tags = Seq.map (fun tag -&amp;gt; new DataCacheTag(tag)) tags

   
type Company =
  { Symbol : string; Name: string; Address : string;  Phone : string; Tags :seq&amp;lt;string&amp;gt; }

// Create a bunch of company info and put it in the "stocks" region
[{Symbol="AAPL"; Name="Appl Inc."; Phone="408-996-1010"; 
  Address="1 Infinite Loop, Cupertino, CA 95014";
  Tags = ["Technology";"Nasdaq";"Personal Computers"]};
 {Symbol="CAT"; Name="Caterpillar, Inc."; Phone="309-675-1000"; 
  Address="100 North East Adams Street, Peoria, IL 61629";
  Tags = ["Dow";"Industrial Goods";"Farm &amp; Construction Machinery"]};
 {Symbol="ACI"; Name="Arch Coal, Inc."; Phone="314-994-2700"; 
  Address="Once City Place Drive, Suite 300, St. Louis, MO 63141";
  Tags = ["Basic Materials";"Industrial Metals &amp; Materials"]};
 {Symbol="HP"; Name="Hewlett-Packard Company"; Phone="650-857-1501"; 
  Address="3000 Hanover Street, Palo Alto, Ca 94304";
  Tags = ["Dow";"Technology"; "Diversified Computer Systems"]};
 {Symbol="JPM"; Name="JP Morgan Chase &amp; Co."; Phone="212-270-6000"; 
  Address="270 Park Avenue, New York, NY 10017";
  Tags = ["Dow";"Financial"; "Money Center Banks"]};
 {Symbol="XOM"; Name="Exxon Mobile Corporation"; Phone="972-444-1000"; 
  Address="5959 Las Colinas Boulevard, Irving, TX 75039-2298";
  Tags = ["Dow";"Basic Materials"; "Major Integrated Oil &amp; Gas"]};]
|&amp;gt; Seq.iter (fun item -&amp;gt; cache.Put(item.Symbol, item, (createTags item.Tags),"stocks") |&amp;gt; ignore)


// Get all stocks in "stocks" region as a HashMap 
let stocks = cache.GetObjectsInRegion("stocks")


// Bulk get, only available with defined regions not default region
let myportfolio = ["AAPL";"CAT";"HP";"JPM"]
let mystocks = cache.BulkGet(myportfolio,"stocks")

// Getting objects from cache by tags (again, only available in defined region)
cache.GetObjectsByTag(new DataCacheTag("Dow Jones"),"stocks")


// Getting stuff by all tags (AND filter)
let candidateTags = ["Technology";  "Personal Computers"] |&amp;gt; createTags
cache.GetObjectsByAllTags(candidateTags,"stocks") 

// Getting stuff by any tags (OR filter)
let interestTags = ["Technology";  "Financial"; "Basic Materials"] |&amp;gt; createTags
cache.GetObjectsByAnyTag(interestTags,"stocks")   

// Concurrency policy is strictly done by the client
// No explicit cache server control on concurrency

// Optimistic Currency example
cache.Remove("mykey")
let version = cache.Add("mykey","mystuff")

// Pretend another thread snuck in and modified this cache item.
cache.Put("mykey","changed value")

// This will check that somebody else modified this and throw an exception
cache.Put("mykey","another value",version)

// Pessimistic locking - locks across all cache nodes
let mutable lockHandle:DataCacheLockHandle = null
let item = cache.GetAndLock("mykey",TimeSpan.FromMinutes(60.0), &amp;amp;lockHandle,true)   


// This will throw error due to the first lockHandle
let mutable lockHandle2:DataCacheLockHandle = null

// This will throw an exception as it was already previously locked
let item2 = cache.GetAndLock("mykey",TimeSpan.FromMinutes(60.0),&amp;amp;lockHandle2, true)   

// This will totally ignore locking policies and overwrite things
// Locking is controlled by diligence on the client side.
cache.Put("mykey","no enforced concurrency on the server")


// Finally, this will update the data and unlock this item in the cache
// Need to redo the GetAndLock for this to work.  Previous statement would wipe out the lock
cache.PutAndUnlock("mykey","new stuff",lockHandle)


// Working with Callbacks
(DataCacheOperations.AddItem,
 fun cacheName regionName key version cacheOperation nd -&amp;gt; 
   printfn "Item added to cache : %s\n" key)
|&amp;gt; cache.AddCacheLevelCallback
|&amp;gt; ignore


// When this is call, it could be seconds or minutes before anything 
// is printed to the console
cache.Add("newkey","new stuff")















&lt;/pre&gt;&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/Q7D2chTCXis" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/2386006987430336213/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=2386006987430336213" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/2386006987430336213?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/2386006987430336213?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/Q7D2chTCXis/f-and-windows-server-appfabric-cache.html" title="F# and Windows Server AppFabric Cache" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2012/03/f-and-windows-server-appfabric-cache.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUUFQHs4eyp7ImA9WhVSFUg.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-3313071912798963533</id><published>2012-03-12T06:00:00.000-07:00</published><updated>2012-03-12T06:00:11.533-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-03-12T06:00:11.533-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint 2010" /><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="f#" /><category scheme="http://www.blogger.com/atom/ns#" term=".NET 3.5" /><title>F# and SharePoint 2010</title><content type="html">&lt;style&gt;
.red { color: #900; }
&lt;/style&gt;
&lt;p&gt;I bought a bunch of SharePoint 2010 books and haven't had the time to go through those books.  Recently, I finally had some time to take look at those books and try it out in F#.  Trying out examples with SharePoint 2007 was fairly painless as I get to do everything in a 32-bit environment.  Trying to get Visual Studio 2010 F# to work with SharePoint 2010 was more challenging.  While I was able to get the compiled F# code to work, I couldn't overcome the hurdles to get SharePoint 2010 to work with F# Interactive, which is really my prefer way to investigate SharePoint server object models.  I can tell from this 
&lt;a href="http://stackoverflow.com/questions/7597426/f-script-sharepoint-2010-api-doesnt-work"&gt;Stack Overflow entry&lt;/a&gt; 
that I was not alone in having trouble to get F# interactive to work with SharePoint API.  The combination of .NET 3.5 framework and 64-bit defeated my effort at getting it to work.  I have tried 
&lt;a href="http://ig2600.blogspot.com/2010/05/making-fsharp-interpreter-fsi-run-in.html"&gt;Igor Dvorkin's steps to run F# interactive in 64-bit&lt;/a&gt; 
and then taking the command line arguments for the F# compiler and apply to F# interactive where applicable.  So I would start up my F# interactive with the following command line parameters:&lt;/p&gt;

&lt;pre&gt;
"C:\Program Files (x86)\Microsoft F#\v4.0\fsi.exe" ^
  --debug:full ^
  --noframework ^
  --define:DEBUG ^
  --define:TRACE ^
  --optimize- ^
  --tailcalls- ^
  -r:"C:\Program Files (x86)\Reference Assemblies\Microsoft\FSharp\2.0\Runtime\v2.0\FSharp.Core.dll" ^
  -r:"C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI\Microsoft.SharePoint.dll" ^
  -r:C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll ^
  -r:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\System.Core.dll" ^
  -r:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll ^
  -r:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.0\System.ServiceModel.dll" 
&lt;/pre&gt;

&lt;p&gt;When I try to run with the above arguments, I get the following error:&lt;/p&gt;

&lt;pre class="red"&gt;
error FS0084: Assembly reference 
'C:\Program Files (x86)\Reference Assemblies\Microsoft\FSharp\2.0\Runtime\v2.0\FSharp.Compiler.Interactive.Settings.dll' 
was not found or is invalid 
&lt;/pre&gt;

&lt;p&gt;So something in &lt;code&gt;FSharp.Compiler.Interactive.Settings.dll&lt;/code&gt; is not working with .NET Framework 3.5.  I wish there was an option in F# interactive that allows me to target the runtime .NET framework to use  (e.g. --target:3.5)&lt;/p&gt;

&lt;p&gt;The good news is that the F# compiler works fine as long as you make sure that you are targeting .NET 3.5 runtime and x64 CPU.  Here's the F# version of a sample code that I pulled from the book
&lt;a href="http://www.amazon.com/exec/obidos/ASIN/0735627460/techie2biz-20"&gt;Inside Microsoft SharePoint 2010&lt;/a&gt; by Ted Pattison, Andrew Connell, Scott Hillier and David Mann&lt;/p&gt;

&lt;pre class="brush: fsharp;"&gt;
open System
open Microsoft.SharePoint

let siteUrl = "http://localhost/"
let sites = new SPSite(siteUrl)

let site = sites.RootWeb
http://www.blogger.com/blogger.g?blogID=18281936#editor/target=post;postID=3313071912798963533
seq {for i in 0..(site.Lists.Count-1) do
       if site.Lists.[i].Hidden &amp;lt;&amp;gt; true then
         yield (site.Lists.[i]) }
|&gt; Seq.iter (fun item -&gt; printf "%s\n" item.Title)
&lt;/pre&gt;

&lt;p&gt;For those who have read my past posts on F# and SharePoint and my past blog post on  &lt;a href="http://jyliao.blogspot.com/2011/11/revisiting-sharepoint-collection.html"&gt;Revisiting the SharePoint collection adapter for F#&lt;/a&gt;, I am sorry to say that I have been led astray by C# example codes and my brain temporary malfunctioned as it produced the SharePoint adapter nonsense in that previous blog post.  There was no need to write any SharePoint utility library in C# to get F# to work with SPListCollection as sequences.  In the above example, I used sequence expressions to generate SPList sequences from SPListCollection.  Another way is to simply create a sequence by use map function as shown in the following example:&lt;/p&gt;

&lt;pre class="brush: fsharp;"&gt;
// Create a sequence by mapping over all items in the collection
let spcollection = site.Lists
[0..spcollection.Count-1] 
|&gt; Seq.map (fun i -&amp;gt; spcollection.[i])
|&gt; Seq.iter (fun item -&amp;gt; printf "%s\n" item.Title)
&lt;/pre&gt;

&lt;p&gt;There was no need to leave the confines of F# development and it is also type safe compared to my previous clumsy attempt at converting &lt;code&gt;SPListCollection&lt;/code&gt; into a sequence.  If you really do want to use an adapter, it could also be implemented in F# as follows:&lt;/p&gt;
&lt;pre class="brush: fsharp;"&gt;
// SPCollectionAdapter written in F#
let fromSPCollection&amp;lt;'a,'b when 'b :&amp;gt; SPBaseCollection&amp;gt; (collection:'b) =
    let enumerator = collection.GetEnumerator()
    let rec enumerate (e:Collections.IEnumerator) acc =
        let flag = e.MoveNext()
        if flag = false then
            acc
        else
            enumerate e (e.Current :?&amp;gt; 'a :: acc)
    enumerate enumerator [] 

// Example usages
fromSPCollection&amp;lt;SPList,SPListCollection&amp;gt; site.Lists
|&amp;gt; Seq.iter (fun item  -&amp;gt; printf "%s\n" item.Title)
    

// webapp is an instance of SPWebApplication object
fromSPCollection&amp;lt;SPContentDatabase,SPContentDatabaseCollection&amp;gt;  webapp.ContentDatabases
|&amp;gt; Seq.iter (fun db -&amp;gt; 
              printf "Content Database : %s\n" db.Name
              printf "Connection String : %s\n" db.DatabaseConnectionString)
    &lt;/pre&gt;&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/oyPnb3D5BYc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/3313071912798963533/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=3313071912798963533" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/3313071912798963533?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/3313071912798963533?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/oyPnb3D5BYc/f-and-sharepoint-2010.html" title="F# and SharePoint 2010" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2012/03/f-and-sharepoint-2010.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEUFSXw7fip7ImA9WhVQFks.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-2445311222455469073</id><published>2012-03-06T06:00:00.000-08:00</published><updated>2012-04-05T14:43:38.206-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-04-05T14:43:38.206-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="Oracle Coherence" /><category scheme="http://www.blogger.com/atom/ns#" term="Clojure" /><title>Adventures in troubleshooting out of memory errors with Coherence cluster.</title><content type="html">&lt;script type="text/javascript" src="https://raw.github.com/sattvik/sh-clojure/master/src/shBrushClojure.js"&gt;&lt;/script&gt;
&lt;p&gt;One day, an application team manager called me and said that their application caused an out of memory error condition in their Oracle Coherence cluster.  This same code base ran in the old Coherence 3.1 environment for months without running into out of memory conditions and now is failing in the new Coherence 3.6 environment in matter of a few weeks on a regular basis.  He said that he had heap dumps and logs and asked whether I could take a look at it and troubleshoot it.&lt;/p&gt;&lt;p&gt;Initially, I was skeptical about being able to help this team manager out.  After all, I know almost nothing about their application code and in all practical terms, I had no previous development experience with Coherence with the exception that I read the book &lt;a href="http://www.amazon.com/gp/product/1847196128?tag=techie2biz-20"&gt;Oracle Coherence 3.5&lt;/a&gt; by Aleksandar Seovic in the past.  My previously participated in testing Coherence performance on VMware and that really did not require me to delve into the Coherence API at all.
&lt;/p&gt;
&lt;p&gt;
Despite these misgivings, I decided to provide my support and told the application team manager that I'll try my best.&lt;/p&gt;
&lt;p&gt;
The system with problems was a multi-node Coherence cluster. When I took a look at the logs, all of them had these similar verbose GC output:&lt;/p&gt;
&lt;pre&gt;
[GC [1 CMS-initial-mark: 1966076K(1966080K)] 2083794K(2084096K), 0.1923110 secs] [Times: user=0.18 sys=0.00, real=0.19 secs] 
[Full GC [CMS[CMS-concurrent-mark: 1.624/1.626 secs] [Times: user=3.22 sys=0.00, real=1.62 secs] 
 (concurrent mode failure): 1966079K-&amp;gt;1966078K(1966080K), 6.6177340 secs] 2084093K-&amp;gt;2084082K(2084096K), [CMS Perm : 13617K-&amp;gt;13617K(23612K)], 6.6177900 secs] [Times: user=8.21 sys=0.00, real=6.62 secs] 
[Full GC [CMS: 1966078K-&amp;gt;1966078K(1966080K), 4.1110330 secs] 2084093K-&amp;gt;2084089K(2084096K), [CMS Perm : 13617K-&amp;gt;13615K(23612K)], 4.1111070 secs] [Times: user=4.11 sys=0.00, real=4.11 secs] 
[Full GC [CMS: 1966078K-&amp;gt;1966078K(1966080K), 4.2973090 secs] 2084092K-&amp;gt;2084087K(2084096K), [CMS Perm : 13615K-&amp;gt;13615K(23612K)], 4.2973630 secs] [Times: user=4.28 sys=0.00, real=4.30 secs] 
[Full GC [CMS: 1966078K-&amp;gt;1966078K(1966080K), 4.1831450 secs] 2084093K-&amp;gt;2084093K(2084096K), [CMS Perm : 13615K-&amp;gt;13615K(23612K)], 4.1831970 secs] [Times: user=4.18 sys=0.00, real=4.18 secs] 
[Full GC [CMS: 1966078K-&amp;gt;1966078K(1966080K), 4.2524850 secs] 2084093K-&amp;gt;2084093K(2084096K), [CMS Perm : 13615K-&amp;gt;13615K(23612K)], 4.2525380 secs] [Times: user=4.24 sys=0.00, real=4.25 secs] 
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid23607.hprof ...
Heap dump file created [2274434953 bytes in 28.968 secs]
&lt;/pre&gt;
&lt;p&gt;This garbage collection log output tells me that they are using CMS for the JVM GC.  The concurrent mode failure entries certainly grabbed my attention.  Normally one would fix concurrent mode failures by tuning the CMS initiation occupancy fraction via -XX:CMSInitiatingOccupancyFraction flag, but in this case, looking that the heap numbers in the lines labeled "Full GC" showed that GC could not clean up any memory at all.  So this problem could not be solved by GC tuning.  By the way, for a great book on tuning garbage collection, I would recommend Charlie Hunt's book &lt;a href="http://www.amazon.com/gp/product/0137142528?tag=techie2biz-20"&gt;Java Performance&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;My next step was to take a look a the heap.  The heap was slightly over 2 GB, which was expected since Coherence cluster node was each configured with a 2GB heap.  Well, that presented a problem for me because I'm still mainly working on a 32-bit Windows laptop.  I needed to find a 64-bit system  with preferably 4 GB of ram or more to look at this. Once I was able to get such a machine and fired up &lt;a href="http://www.eclipse.org/mat/"&gt;Eclipse Memory Analyzer Tool (MAT)&lt;/a&gt;.  Once I looked at the heap, it was pretty obvious what was the biggest memory offender. The biggest memory offender was a top level hashmap chewing up 1.6 GB of memory.  Delving further into that hash map structure, it reveals that Coherence caching structure is a hash of hashes.  Looking at the hashes, I notice that there were over 2000+ items in the top level hash.   That would imply that there were over 2000+ caches in the Coherence cluster.  Studying each individual cache, I would notice cache names like&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;alpha-FEB-21&lt;/li&gt;
&lt;li&gt;alpha-FEB-22&lt;/li&gt;
&lt;li&gt;alpha-FEB-23&lt;/li&gt;
&lt;li&gt;alpha-FEB-24&lt;/li&gt;
&lt;li&gt;beta-FEB-21&lt;/li&gt;
&lt;li&gt;beta-FEB-22&lt;/li&gt;
&lt;li&gt;beta-FEB-23&lt;/li&gt;
&lt;li&gt;beta-FEB-24&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and so forth.  I ask the application team manager whether he expected
to have this many caches in the cluster.  The application team manager said no; he expected a much smaller set of caches.  The application normally destroy caches older than 2 days.  The developers provided me their code related to the creation and destruction of caches and I saw the following lines of code and it seems pretty innocuous:&lt;/p&gt;

&lt;pre class="brush: java;"&gt;
    public static void destroyCache(String name) {
        Collection listOfCacheNames = getListOfCacheNames(name, false);
        Iterator iterator = listOfCacheNames.iterator();
        while (iterator.hasNext()) {
            String name = (String) iterator.next();
            NamedCache namedCache = CacheFactory.getCache(name);
            namedCache.destroy();
        }
    }
&lt;/pre&gt;

&lt;p&gt;
I went back to the memory analyzer tool and performed a GC to root analysis and saw the top level object that's holding onto this heap as:&lt;/p&gt;
&lt;pre&gt;
com.tangosol.coherence.component.net.Cluster$IpMonitor @ 0x77ff4b18 &lt;/pre&gt;
&lt;p&gt;with the label "Busy Monitor" next to it.  This line item seems to suggest that there's a monitor lock on this cache.  Looking at the Coherence API documentation, I see the following entry:&lt;/p&gt;
&lt;hr/&gt;
&lt;h3&gt;destroy&lt;/h3&gt;
&lt;pre class="oac_no_warn"&gt;
void &lt;b&gt;destroy&lt;/b&gt;()
&lt;/pre&gt;
&lt;dl&gt;
&lt;dd&gt;Release and destroy this instance of NamedCache.
&lt;p&gt;&lt;b&gt;Warning:&lt;/b&gt; This method is used to completely destroy the specified cache across the cluster. All references in the entire cluster to this cache will be invalidated, the cached data will be cleared, and all internal resources will be released.&lt;/p&gt;
&lt;p&gt;Caches should be destroyed by the same mechansim in which they were obtained. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;new Cache() - cache.destroy()&lt;/li&gt;
&lt;li&gt;CacheFactory.getCache() - CacheFactory.destroyCache()&lt;/li&gt;
&lt;li&gt;ConfigurableCacheFactory.ensureCache() - ConfigurableCacheFactory.destroyCache()&lt;/li&gt;
&lt;/ul&gt;
Except for the case where the application code expicitly allocated the cache, this method should not be called by application code.&lt;/dd&gt;
&lt;/dl&gt;
&lt;hr/&gt;
&lt;p&gt;
Looking at this documentation, we initially thought that since the cache was obtained via CacheFactory and therefore should be destroyed via CacheFactory ergo CacheFactory had a monitor lock on the underlying collections.  The code provided by the developers used one mechanism to create the cache and another mechanism to destroy the cache so we presume that was the problem.  So I implemented a test script to test out that theory and surprisingly, even destroying via CacheFactory, I still encounter out of memory issues.  Only by clearing the cache before destroying the cache was I able to avoid out of memory errors. Here's the script that I developed in Clojure to test my theories:&lt;/p&gt;
&lt;pre class="brush: clojure;"&gt;
(import '(org.apache.commons.lang3 RandomStringUtils) 
        '(java.math BigInteger)
        '(java.util Random Date HashMap)
        '(com.tangosol.net NamedCache CacheFactory CacheService Cluster))

(defn random-text [] (RandomStringUtils/randomAlphanumeric 1048576))
(defn random-key [] (RandomStringUtils/randomAlphanumeric 12))
        
(CacheFactory/ensureCluster)
(def buffer (new HashMap))

(defn print-horizontal-line [c] (println (apply str (repeat 80 c))))

(def caches '("alpha" "beta" "gamma" "delta" 
              "epsilon" "zeta" "eta" "theta" 
              "iota" "kappa" "lambda" "mu" "nu"
              "xi", "omicron" "pi" "rho"
              "signma" "tau" "upsilon" "phi", 
              "chi" "psi" "omega"))

(defn load-cache [cache-name n]
    (let [cache (CacheFactory/getCache cache-name)]
         (print-horizontal-line  "=")
         (println "Creating cache : " cache-name)
         (print-horizontal-line  "=")         
         (.clear buffer)
         (dotimes [_ n] (.put buffer (random-key) (random-text)))
         (.putAll cache buffer)))


(defn recreate-oom-problem [cache-name]
    (let [cache (CacheFactory/getCache cache-name)]
         (load-cache cache-name 200)
         (.destroy cache)))
         
(defn try-fix-oom-1 [cache-name]
    (let [cache (CacheFactory/getCache cache-name)]
         (load-cache cache-name 200)
         (CacheFactory/destroyCache cache)))

(defn try-fix-oom-2 [cache-name]
    (let [cache (CacheFactory/getCache cache-name)]
         (load-cache cache-name 200)
         (.clear cache)
         (CacheFactory/destroyCache cache)))
         
; Test run recreation of original problem.  Was able to reproduce OOM issues         ; 
(doseq [cache caches] (recreate-oom-problem cache))

; Surprise! Still have OOM issues
(doseq [cache caches] (try-fix-oom-1 cache))

; No longer have OOM issues, but memory is still leaking (slowly)
(doseq [cache caches] (try-fix-oom-2 cache))
         
&lt;/pre&gt;
&lt;p&gt;However, I still suspect memory leaks, it's just that my memory leak is a lot smaller now.  To verify that I had a memory leak, I would run my Clojure test script and the deliberately create and fill a cache without clearing it.  I then forced a full garbage collection followed by a heap dump.  In memory analyzer tool, I would look up the cache that I did not clear, and list all the incoming references.  Then I would look for a HashMap in the incoming references and select one of those and check for outgoing references.  And in that outgoing references, I could see that the key contains the name of a cache that I had called &lt;code&gt;CacheFactory.destroyCache()&lt;/code&gt; on and the retained heap sizes range anywhere from 24 to 160 with the sizes that seems proportional to the size of the cache name.&lt;/p&gt;&lt;p&gt;In conclusion, it would seem Oracle Coherence does have a memory leak issues with the cache creation and destruction process. If we clear the cache before destroying the cache, I suspect it would be a long time before the memory leak is even noticeable by this particular application.&lt;/p&gt;
&lt;p&gt;To verify that this leak did not exist in the older 3.1 version, we ran this test code on and and was unable to reproduce the out of memory errors.   We also tested this against Oracle Coherence 3.7.1 and was unable to reproduce the out of memory error.  So, it looks like that this memory error is specific to Oracle Coherence 3.6 only.&lt;/p&gt;
&lt;p&gt;Throughout this entire process, I thought that the secret sauce that enabled me to quickly learn Coherence, reproduce and troubleshoot the Coherence out of memory problem was Clojure.  Clojure allowed me to interactively manipulate Coherence clusters and explore the API, which would have been a lot slower if I had to go through the normal edit-compile-run cycle with plain old Java.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/KhhZbIaFvmM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/2445311222455469073/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=2445311222455469073" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/2445311222455469073?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/2445311222455469073?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/KhhZbIaFvmM/adventures-in-troubleshooting-out-of.html" title="Adventures in troubleshooting out of memory errors with Coherence cluster." /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>2</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2012/03/adventures-in-troubleshooting-out-of.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CE8ER309cSp7ImA9WhRaGE4.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-522394047895772364</id><published>2012-02-21T06:00:00.000-08:00</published><updated>2012-02-21T06:00:06.369-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-21T06:00:06.369-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="reuse" /><category scheme="http://www.blogger.com/atom/ns#" term=".NET Framework" /><category scheme="http://www.blogger.com/atom/ns#" term="C#" /><title>Coding for reuse versus coding for specific use</title><content type="html">&lt;p&gt;One day, a C# developer came to me asked me to troubleshoot their application, which involves some data transformation code.  In the course of troubleshooting this particular application's runtime issue, I saw this particular code implementation:&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;
private string[] SplitTextwithSpecialCharacter(string lineFromGUI, int splitLength) 
{

    List&amp;lt;string&amp;gt; lines = new List&amp;lt;string&amp;gt;();
    int start = 0;


    while (start &amp;lt; lineFromGUI.Length)
    {
        string temp = string.Empty;
      
        //if length is longer it will throw exception so size the last segment appropriately
        string line = lineFromGUI.Substring(start, Math.Min(splitLength, lineFromGUI.Length - start));
        
        //temp = line.Replace(SpecialCharacter, string.Empty);

        temp = line.Replace("^", ""); //New 6/30
        if (temp.Length &amp;gt; 0) 
        {
            //lines.Add(line.Replace(SpecialCharacter, string.Empty));
            lines.Add(temp);
        }
        //lines.Add(line);
        start += splitLength;
    }
    return lines.ToArray();
}
&lt;/pre&gt;
&lt;p&gt;When I saw this code, I inwardly cringed due to the very narrow scope in the code implementation to solve a seemingly generic problem.  This code screams for refactoring to me.  From the commented out section of the code, I could see that one of the developers tried to implement some reuse before reverting back to the specific use.&lt;/p&gt;
&lt;p&gt;I see a couple of issues with this particular implementation.  The first issue being that this method is trying to do two very different things; one is trying to split the text into lines and the second is removing all the caret characters from the text.  In my opinion, these 2 different operations should be separated out into 2 different methods.  The second issue is that the caret character removal function should be generalized such that you should be able to remove any character or perhaps even a set of characters beyond just the caret symbol.  In the specific implementation, what would happen if they needed code to remove an asterisk instead of caret?  What would happen if they need to remove more than one special character?  Do they add additional methods?  Do they modify the existing method?  If they modify the existing method to take parameters, then they would have to change all the existing client code that calls this method.  So it would seem to be easier for future maintenance to go ahead and make these 2 functions reusable.&lt;/p&gt;&lt;p&gt;Here's how I would refactor this particular method into some utilities namespace and use extension methods to add capability to the string object.  Here's how that code would look like:&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;
public static class Extensions
{
    public static string FilterOut(this string line, ISet&amp;lt;char&amp;gt; filter)
    {

        if (line == null ||  filter == null || filter.Count == 0) return line;


        return new String(line.Where(c =&amp;gt; !filter.Contains(c)).ToArray());
    }


    public static IList&amp;lt;string&amp;gt; SplitIntoLines(this string data, int splitLength) 
    {

        // Check preconditions
        if (data == null) throw new NullReferenceException();

        if (splitLength &amp;lt;= 0) throw new IndexOutOfRangeException("Must be greater than 0!");

        IList&amp;lt;string&amp;gt; lines = new List&amp;lt;string&amp;gt;();

        while (data.Length &amp;gt; splitLength)
        {
            string line = data.Substring(0,splitLength);
            data = data.Substring(splitLength, data.Length-splitLength);
            lines.Add(line);
        }
        lines.Add(data);
        return lines;
    }
}
&lt;/pre&gt;&lt;p&gt;With this extension method, you can now use this utility method as follows:&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;
int linesize = 30;
ISet&amp;lt;char&amp;gt; filter = new HashSet&amp;lt;char&amp;gt; { '^', '*', '&amp;', '$' };
string[] results = data.SplitIntoLines(linesize)
                       .Select(line =&gt; line.FilterOut(filter))
                       .ToArray();
&lt;/pre&gt;&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/ffBau3Y3gqc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/522394047895772364/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=522394047895772364" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/522394047895772364?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/522394047895772364?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/ffBau3Y3gqc/coding-for-reuse-versus-coding-for.html" title="Coding for reuse versus coding for specific use" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2012/02/coding-for-reuse-versus-coding-for.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkUFR3cyfSp7ImA9WhRbE0o.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-4559158457660466923</id><published>2012-02-04T07:43:00.000-08:00</published><updated>2012-02-04T07:43:36.995-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-04T07:43:36.995-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ruby" /><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="jar" /><category scheme="http://www.blogger.com/atom/ns#" term="packaging" /><title>Detect multiple occurrences of Java classes in jar files</title><content type="html">&lt;style&gt;
#mytable {
 width: 700px;
 padding: 0;
 margin: 0;
}

caption {
 padding: 0 0 5px 0;
 width: 700px;  
 font: italic 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
 text-align: right;
}

th {
 font: bold 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
 color: #4f6b72;
 border-right: 1px solid #C1DAD7;
 border-bottom: 1px solid #C1DAD7;
 border-top: 1px solid #C1DAD7;
 letter-spacing: 2px;
 text-transform: uppercase;
 text-align: left;
 padding: 6px 6px 6px 12px;
 background: #CAE8EA url(images/bg_header.jpg) no-repeat;
}

th.nobg {
 border-top: 0;
 border-left: 0;
 border-right: 1px solid #C1DAD7;
 background: none;
}

td {
 border-right: 1px solid #C1DAD7;
 border-bottom: 1px solid #C1DAD7;
 background: #fff;
 padding: 6px 6px 6px 12px;
 color: #4f6b72;
}


td.alt {
 background: #F5FAFA;
 color: #797268;
}

th.spec {
 border-left: 1px solid #C1DAD7;
 border-top: 0;
 background: #fff url(images/bullet1.gif) no-repeat;
 font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
}

th.specalt {http://www.blogger.com/post-edit.g?blogID=18281936&amp;postID=4559158457660466923
 border-left: 1px solid #C1DAD7;
 border-top: 0;
 background: #f5fafa url(images/bullet2.gif) no-repeat;
 font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
 color: #797268;
}
&lt;/style&gt;
&lt;p&gt;A developer came to me for help and said that a recent upgrade to a newer Java library broke the code.  The developer was getting was a class not found exception.  The developer said that the old jar file was replaced with a newer jar file.  I know this same library upgrade in other applications did not have any problems, so this sounded like an issue with duplicate occurrence of the same class in the classpath.  Since my &lt;a href="http://www.amazon.com/exec/obidos/ASIN/0596516177/techie2biz-20"&gt;Ruby Programming Language&lt;/a&gt; book was in handy access when this developer came to me, I ended up writing a small ruby script that went through all the jar files packed in the web archive and dumped an output of the fully qualified class along with the jar file that it can be found in and a count of the number of occurrences.  Here's the ruby script running on a Windows platform:
&lt;/p&gt;
&lt;pre class="brush: ruby;"&gt;
java_home="C:\\sdk\\jdk1.6.0_24\\bin"

classmaps = Hash.new(0)

Dir["*.jar"].each do |file|
  cmd = sprintf("%s\\jar  -tf %s",java_home,file)
  lines = `#{cmd}`
  lines.each do |line|
    if line =~ /.class/
      key = line.chomp
      if classmaps.key?(key) then
        old = classmaps[key]
        data = sprintf("%s,%s",old,file)
        classmaps[key] = data 
      else
        classmaps[key] = file
      end
    end
  end
end


classmaps.each do |k,v|
    tokens = v.split(",")
    printf("%s,%i,%s\n",k,tokens.size,v)
end

&lt;/pre&gt;&lt;p&gt;I then loaded the output file in csv format into Excel and sorted the occurrences in descending order and was flabbergasted to find numerous entries that look something like the following entry:&lt;/p&gt;
&lt;table&gt;
&lt;tr&gt;&lt;th&gt;Class Name&lt;/th&gt;&lt;th&gt;Occurrence&lt;/th&gt;&lt;th colspan=6&gt;Found in Jar files:&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;kodo/jdo/KodoExtent.class&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;mylib.jar&lt;/td&gt;&lt;td&gt;mylib.jar&lt;/td&gt;&lt;td&gt;mylib.jar&lt;/td&gt;&lt;td&gt;kodo-api.jar&lt;/td&gt;&lt;td&gt;kodo-runtime.jar&lt;/td&gt;&lt;td&gt;kodo.jar&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;Somehow, for this application team, they were able to add the same class to the same jar file multiple times.  I never thought that it was possible to add the same class to the same jar file multiple times nor would I ever want to do that.  When I finally confronted the application team about this, they recognize that their build process is broken and need to fix their build process.  But as a quick test, the developer removed some of the duplicate classes related to the library upgrade and the problems went away.  Until Java 8 is introduced with Modularity capabilities, this tool has will be a handy way for me to check duplicate classes given a list of jar files.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/a_dDztcKy3w" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/4559158457660466923/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=4559158457660466923" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/4559158457660466923?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/4559158457660466923?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/a_dDztcKy3w/detect-multiple-occurrences-of-java.html" title="Detect multiple occurrences of Java classes in jar files" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2012/02/detect-multiple-occurrences-of-java.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkABRn46fSp7ImA9WhRUEUw.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-7405520043120060073</id><published>2012-01-20T17:52:00.000-08:00</published><updated>2012-01-20T17:52:37.015-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-20T17:52:37.015-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="f#" /><title>Translating If-Then-Else Control Flow Idiom to F#</title><content type="html">&lt;p&gt;I was reading through Juval L&amp;#246;wy's &lt;a href="http://www.amazon.com/exec/obidos/ASIN/0596805489/techie2biz-20"&gt;Programming WCF Services&lt;/a&gt; book and wondering if I should do a series of WCF blog posts in F# based on L&amp;#246;wy's book when I ran into a common construct found in C# programs.  That construct looks something like the following C# code:
&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;
    public static void MyMethod(String oldstuff, String newstuff, bool flag)
    {
        if (oldstuff == null)
         throw new Exception("oldstuff is null!");

        if (newstuff == null)  {
            DoSomething("Default");
            return;
        }
        if (flag == false)  {
            DoSomething(oldstuff);
            return;
        }
        DoSomething(newstuff);
    }

&lt;/pre&gt;
&lt;p&gt;This is a construct that I oftened have used in the past and have never thought about it much.  But when you translate the above code directly into F#, it becomes a lot more verbose because F# requires you to implement the &lt;code&gt;then&lt;/code&gt; clause.  A direct translation to F# as follows:&lt;/p&gt;
&lt;pre class="brush: fsharp;"&gt;
let mymethod oldstuff newstuff flag =
    if oldstuff = null then 
        raise (new Exception("oldstuff is null!"))
    else
        if newstuff = null then
            DoSomething("Default")
        else
            if flag = false then
                DoSomething(oldstuff)
            else
                DoSomething(newstuff)
&lt;/pre&gt;
&lt;p&gt;If I had a lot of these if-then-else statements in my C# method, then my F# version would disappear off the screen to the right if I tried to implement it by direct translation.  I thought about how I could implement this in F# and came up with this following possibility:&lt;/p&gt;
&lt;pre class="brush: fsharp;"&gt;
let revised_mymethod oldstuff newstuff flag =
    let (_,action) =
        [(oldstuff=null,  lazy (raise (new Exception("oldstuff is null!"))));
         (newstuff=null,  lazy (DoSomething("Default")));
         (flag=false,     lazy (DoSomething(oldstuff)));
         (flag=true,      lazy (DoSomething(newstuff)))]
        |&amp;gt; List.filter fst
        |&amp;gt; List.head
    action.Force()
&lt;/pre&gt;
&lt;p&gt;Rewriting the C# code in this fashion makes me think of rules engines and after refactoring out some common code, I could rewrite the above F# code as follows:&lt;/p&gt;
&lt;pre class="brush: fsharp;"&gt;
let followrules (xs:(bool*Lazy&amp;lt;unit&amp;gt;) list) =
    (xs |&amp;gt; List.filter fst |&amp;gt; List.head |&amp;gt; snd).Force()

let revised_mymethod2 oldstuff newstuff flag =
    [(oldstuff=null , lazy (raise (new Exception("oldstuff is null!"))));
     (newstuff=null,  lazy (DoSomething("Default")));
     (flag=false,     lazy (DoSomething(oldstuff)));
     (flag=true,      lazy (DoSomething(newstuff)))]
    |&amp;gt; followrules

&lt;/pre&gt;
&lt;p&gt;With this new construct, I can easily re-arrange the order of evaluation, add or remove new conditions.  This new construct just seems to have more advantages than the old if-then-else construct in F#.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/0IsHRc93W1k" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/7405520043120060073/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=7405520043120060073" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/7405520043120060073?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/7405520043120060073?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/0IsHRc93W1k/translating-if-then-else-control-flow.html" title="Translating If-Then-Else Control Flow Idiom to F#" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>4</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2012/01/translating-if-then-else-control-flow.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEQFQXk-cCp7ImA9WhVQFks.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-7001345717667108870</id><published>2012-01-11T10:32:00.000-08:00</published><updated>2012-04-05T14:45:10.758-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-04-05T14:45:10.758-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Coherence" /><category scheme="http://www.blogger.com/atom/ns#" term="Oracle" /><category scheme="http://www.blogger.com/atom/ns#" term="Clojure" /><category scheme="http://www.blogger.com/atom/ns#" term="Performance" /><title>Testing Coherence with Clojure</title><content type="html">&lt;script type="text/javascript" src="https://raw.github.com/sattvik/sh-clojure/master/src/shBrushClojure.js"&gt;&lt;/script&gt;
&lt;p&gt;A developer came to me the other day asking for help in diagnosing some issues with their application and the interaction with Oracle's Coherence product.  I wanted to write some testing harness to quickly test some Coherence configuration and gave some thought about how I would go and try to replicate the issues that the application had.  I wanted a REPL environment so that I can interactive manipulate the Coherence API and dump outputs on demand.  I decided to use Clojure to experiment with Coherence, although I could have used JRuby, Jython, Groovy or Scala.  From purely a familiarity perspective, I would rank my usage of these listed languages in the order of Ruby first, Python second, Groovy third, Clojure fourth and Scala last.  But for some unknown, deep-seated and probably emotional reasons, I like Clojure more and relish the opportunity to use it.  One of the first thing I tried with Clojure and Coherence is to perform a timing test on adding data to a 2 node distributed cache  in serial vs concurrent mode.  Here's the example Clojure script:&lt;/p&gt;
&lt;pre class="brush: clojure;"&gt;
(import '(org.apache.commons.lang3 RandomStringUtils) 
        '(java.math BigInteger)
        '(java.util Random Date HashMap)
        '(com.tangosol.net NamedCache CacheFactory CacheService Cluster))

(CacheFactory/ensureCluster)
(def cache (CacheFactory/getCache "sandbox"))

(defn random-text [] (RandomStringUtils/randomAlphanumeric 1048576))
(defn random-key [] (RandomStringUtils/randomAlphanumeric 12))


; Testing serial puts
(new Date)
(dotimes [_ 200] (.put cache (random-key) (random-text)))
(new Date)

; Testing concurrent puts
(def buffer (new HashMap))
(new Date)
(dotimes [_ 200] (.put buffer (random-key) (random-text)))
(.putAll cache buffer)
(new Date)
&lt;/pre&gt;
&lt;p&gt;Running this code showed a 2x gain in speed of data load.  On one of the Coherence nodes, I had JVisualVM connected to it and watched the realtime GC behaviors with VisualGC.  It has been fascinating to watch the behaviorial differences between serial vs parallel data load and the memory activities of the Coherence node when my Clojure script was idle.  I hope to conduct more tests in the future looking at GC behaviors and leverage my Clojure script as load driver in my testing efforts and assist me in GC tuning of Coherence instances.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/x4a4Mu_coWY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/7001345717667108870/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=7001345717667108870" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/7001345717667108870?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/7001345717667108870?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/x4a4Mu_coWY/testing-coherence-with-clojure.html" title="Testing Coherence with Clojure" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2012/01/testing-coherence-with-clojure.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkIFSXc7fSp7ImA9WhRSEEg.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-4291855959070754930</id><published>2011-11-11T16:41:00.000-08:00</published><updated>2011-11-11T16:41:58.905-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-11T16:41:58.905-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint 2007" /><category scheme="http://www.blogger.com/atom/ns#" term="f#" /><title>Revisiting the SharePoint collection adapter for F#</title><content type="html">&lt;p&gt;Recently, I got to work with SharePoint 2007 again and I revisited a previously written blog &lt;a href="http://jyliao.blogspot.com/2008/09/exploring-sharepoint-2007-object-model.html"&gt;Exploring SharePoint 2007 Object Model with F#&lt;/a&gt;.  In that blog, I complained about the fact that SharePoint collections did not implement IEnumerable, causing me to implement a type specific collection adapter as pilfered from &lt;a href="http://asadewa.wordpress.com/2008/01/03/linq-ing-splistcollection/"&gt;Asfar Sadewa&lt;/a&gt;.  However, as I started to work with more different SharePoint collections, I realized that I need a generic SharePoint collection adapter.  But once again, I was foiled by SharePoint's library designers.  I wanted to ensure some level of type safety for my generic collection adapter and wanted to bound the collection item generic type variable so the client code is restricted to types such as &lt;code&gt;SPWeb&lt;/code&gt;, &lt;code&gt;SPList&lt;/code&gt;, etc.  Looking up the hierarchy, I see the type &lt;code&gt;SPSecurableObject&lt;/code&gt;.  Unfortunately, SharePoint library designers did not make &lt;code&gt;SPSecurableObject&lt;/code&gt; visible to others and the next level up that hierarchy is the Object type.  So I'm stuck with an unbounded generic type for the collection item in my generic version SharePoint collection adapter. Actually, what I really want is to ensure that the collection item type is consistent with the collection type.  I haven't quite figure out if that's even possible to apply that kind of type parameter constraints in C#.  Here is generic version of the SharePoint collection adapter (with the runtime type error leakages) implementation:
&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;
using System;
using System.Collections.Generic;
using Microsoft.SharePoint;

namespace SharePoint.Utility
{
    // I really wanted to apply constraint to TPart to SPSecurableObject
    public class SPCollectionAdapter&amp;lt;TPart, TCollection&amp;gt; : List&amp;lt;TPart&amp;gt; where TCollection : SPBaseCollection
    {
        private TCollection _listCol;

        public SPCollectionAdapter(TCollection listCol)
        {
            _listCol = listCol;
            Refresh();
        }

        private void Refresh()
        {
            this.Clear();
            this.Capacity = _listCol.Count;

            foreach (TPart item in _listCol)
            {
                this.Add(item);
            }
        }

    }
}
&lt;/pre&gt;
&lt;p&gt;This allows me to write my F# code as follows:&lt;/p&gt;

&lt;pre class="brush: fsharp;"&gt;
open Microsoft.SharePoint
open SharePoint.Utility

let path="http://localhost/"
let collection = new SPSite(path)
let site = collection.RootWeb
let lists = SPCollectionAdapter&amp;lt;SPList,SPListCollection&amp;gt;(site.Lists)
Seq.iter (fun (x:SPList) -&gt; printf "%s\n" x.Title) lists

&lt;/pre&gt;&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/CcJnbODvXgw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/4291855959070754930/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=4291855959070754930" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/4291855959070754930?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/4291855959070754930?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/CcJnbODvXgw/revisiting-sharepoint-collection.html" title="Revisiting the SharePoint collection adapter for F#" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2011/11/revisiting-sharepoint-collection.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUYGQXo8eyp7ImA9WhZXGUU.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-9108143386336875981</id><published>2011-05-09T17:24:00.000-07:00</published><updated>2011-05-09T17:25:20.473-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-05-09T17:25:20.473-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="haskell" /><title>Porting the Log Analysis Code to Haskell</title><content type="html">&lt;p&gt;My coworker approached me the other day and ask what open source log analysis tools would I recommend.  I personally do not have much experience with a general purpose open source log analysis tools so I would have probably recommended him to take a look at &lt;a href="http://www.splunk.com/"&gt;Splunk&lt;/a&gt;.  Since I've recently written a customized log analysis software, I became curious and asked him what he intend to do with the log analysis software.&lt;/p&gt; &lt;p&gt;My coworker said that he needed to analyze Tibco EMS logs.  Tibco EMS logs incoming messages in the order it receives.  My coworker is interested in a set of related messages that is identified by the message ID tag.  His particular issue is that the logs entries that he's interested in are interspersed with other log entries that he's not interested.  He wanted a log file where the log entries are grouped by message id in historical order.&lt;/p&gt;&lt;p&gt;Once I understood his needs, I realize that he did not need the Splunk and that I could quickly adapt my F# log analysis software written in the previous blog post for his need.  When I gave him the modified F# code, he asked me if I could port it to Linux.  That threw me for a loop.  I briefly entertained the idea of building a Mono system and compile F# on Mono but decided against it for now.  I thought it would be easier to just port it to Haskell, which I already have on Linux.&lt;/p&gt;&lt;p&gt;Here's the ported Haskell log analysis software with modifications to work with Tibco log entries.&lt;/p&gt;
&lt;p&gt;
&lt;pre class="brush: haskell;"&gt;

import Data.Time.Calendar
import Data.Time.LocalTime
import Data.Time.Parse
import List
import System.Environment

type Category = String
type Entry = [String]
type TimeStamp = (LocalTime,String)
type LogHeader = (TimeStamp, Category)

alphaTime = LocalTime (fromGregorian 2000 1 1) midnight 

data LogEntry = LogEntry (TimeStamp, String) [String]
                deriving (Show)

{- Grab label -}
categorize (_ : _ : label : _) = label
categorize words = ""

{- Grab timestamp -}
timestamp (date : time : _ )  = strptime "%Y-%m-%d %H:%M:%S" (date ++ " " ++ time)
timestamp words  = Nothing
           
{- header :: String -&gt; (String, Maybe (LocalTime, String)) -}           
header line = (timestamp tokens, categorize tokens)    
    where tokens = words line

{- Concrete implementation of Tibco log parser -}    
logparser :: [String] -&gt; LogHeader -&gt; [String] -&gt; [LogEntry] -&gt; [LogEntry]
logparser (line : rest) xheader entry entries  = process (header line)    
    where process (Just (ts),label) = 
              logparser rest h [line] ((LogEntry xheader (reverse entry)):entries ) where h = (ts,label)
          process (Nothing,_) = logparser rest xheader (line : entry) entries

logparser [] xheader entry entries = reverse ((LogEntry xheader entry) : entries) 

{- Utility method  to pull items out of LogEntry -}
entry (LogEntry _ entries) = entries    
category (LogEntry (_,label) _) = label

{- comparator based on category -}
categorysort (LogEntry (_,a) _) (LogEntry (_,b) _) 
    | a &gt; b = GT
    | a &lt; b = LT
    | otherwise = EQ

parselog parser lines = parser lines ((alphaTime,".000"),"STARTFLAG") [] []
    
processlog = unlines
             . map (unlines . entry)
             . sortBy categorysort
             . (parselog logparser)
             . lines 

main = do (filename:_) &lt;- getArgs
          contents &lt;- readFile filename
          putStr (processlog contents)

&lt;/pre&gt;
&lt;/p&gt;
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/z481Bk3q5Y0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/9108143386336875981/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=9108143386336875981" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/9108143386336875981?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/9108143386336875981?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/z481Bk3q5Y0/porting-log-analysis-code-to-haskell.html" title="Porting the Log Analysis Code to Haskell" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2011/05/porting-log-analysis-code-to-haskell.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEYMSHcyeip7ImA9WhZTF04.&quot;"><id>tag:blogger.com,1999:blog-18281936.post-5510903789813899054</id><published>2011-03-21T12:36:00.000-07:00</published><updated>2011-03-21T12:36:29.992-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-21T12:36:29.992-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="functional programming" /><category scheme="http://www.blogger.com/atom/ns#" term="f#" /><title>Log Analysis with F#</title><content type="html">&lt;p&gt;I am often pulled into investigative teams to resolve performance issues.  In one particular instance, I was involved in troubleshooting a Java-based batch job that had experience performance issue after it migrated from an older database platform to a newer database platform.  Typically in these performance related issues, I would analyze the logs to see if I can spot tell tale signs of performance issues.  Some of these log files can range up to millions of lines of text.  In this particular case, I received a log file with of almost 4 million lines of text.  I do not want to wade through these log files by hand.  In the past, I would have use a scripting language like perl or ruby to help parse these logs.  But lately, I have leaned more towards F# to analyze log files.  The following describes my experience with developing my log analysis software in F#.&lt;/p&gt;&lt;p&gt;The log files that I have received have sample entries with the following pattern:&lt;/p&gt;&lt;p&gt;&lt;pre&gt;
02/04/11 21:15:55 xyz 123 CATEGORY_LABEL Lorem ipsum dolor sit amet 
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
    Aliquam varius mollis purus, vel molestie lorem pellentesque sed. 
&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;where CATEGORY_LABEL categorizes the jobs performed and each log entry always starts with a timestamp.  Typically, multiple jobs are running at the same time so the log entries have data interspersed. To start this effort, I want to capture the timestamp information, the category label and the entire log entry.  To model this, I created the following type to capture a log entry:&lt;/p&gt;&lt;p&gt;&lt;pre class="brush: fsharp;"&gt;

type LogEntry =
    {
        Timestamp : DateTime;
        Category  : string;
        Entry     : string list;
    }
&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;I want to be able to easily access Timestamp and Category label information while retaining the entire log entry for that timestamp/category.  I decided to model the log entry as a list of strings.  I had hope to leverage list operations later on to perform filtering capability.  One of the drawbacks to this model for log entry is if the log file had text that split across 2 lines.&lt;/p&gt;&lt;p&gt;The first problem is to extract the timestamp and the category information.  To capture the timestamp information, I implemented the following functions:&lt;/p&gt;&lt;p&gt;&lt;pre class="brush: fsharp;"&gt;

let parsedate datestring = DateTime.TryParse(datestring)

let getTimestamp (words:string []) =
    if words.Length &gt; 2 then
        let timestamp = sprintf "%s %s" words.[0] words.[1]
        match parsedate timestamp with
            | true, d -&gt; Some(d)
            | false,_ -&gt; None
    else None
&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;I leveraged F#'s Option type to return either a valid timestamp or None if no timestamp are found. You can see later how I use this as a filter to determine if I'm at the start of a log entry or not. Here's the code to get the category label:&lt;/p&gt;
&lt;p&gt;&lt;pre class="brush: fsharp;"&gt;
let getCategory (words:string []) =
    if words.Length &gt; 4 then 
        let tokens = words.[4].Split[|':'|]
        tokens.[0]
    // Once in a while, there is log entry with no labels
    else ""
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;I know that the category label always is the 5th token, but once in a while the category label shows up in the log as CATEGORY_LABEL:Lorem, therefore I have to split the word with the token ":" to extract just the category label.  &lt;/p&gt;&lt;p&gt;With these 2 functions, I can now build my getheader function which would extract the timestamp and category label:&lt;/p&gt;
&lt;p&gt;&lt;pre class="brush: fsharp;"&gt;
    
// Get the timestamp and the job category     
let getHeader (line:string) =
    let words = line.Trim().Split[| ' ' |]
    let timestamp = getTimestamp words
    let category = getCategory words
    (timestamp,category)
&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;I now have all the pieces to be able to build my log parser:    &lt;/p&gt;&lt;p&gt;&lt;pre class="brush: fsharp;"&gt;

// Utility function to create log entry
let createLogEntry (timestamp,category)  entry =
    {Timestamp = timestamp;  Category=category;  Entry = List.rev entry}      
    
    
// Core parser engine to parse the log files
let rec logparser lines oldheader entry (acc:LogEntry list) =
    match lines with
    | line :: rest -&gt; 
        // Check with we got new timestamp with this line
        let header = getHeader line
        match header with
        | (Some(ts),category) -&gt; 
            // Found new timestamp,create log entry from the collected lines
            let logentry = createLogEntry oldheader (line::entry)
            //  Begin new log entry collections
            logparser rest (ts,category) [] (logentry::acc)
        | (None,_) -&gt; 
            // Add new entry to line
            logparser rest oldheader (line::entry) acc
    // We're done with all line processing!
    | [] -&gt; 
        let logentry = createLogEntry oldheader entry
        logentry :: acc |&gt; List.rev
&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;This log parser would recursively processed each line in the log file and build a list of LogEntry to return.  When I find a header, I would begin a new LogEntry.  Because I built the list using the :: (cons) operator, I need to reverse the list when I finish building the list.   I would use the above parser engine as shown in the following code, which would return to me list of all log entries. &lt;/p&gt;&lt;p&gt;&lt;pre class="brush: fsharp;"&gt;
let parselog file parser =
    let lines = File.ReadAllLines(file)
    let past = DateTime.Now
    parser (Array.toList lines) (past,"") [] []
&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;Now I have all the pieces to begin performing log analysis.  I typically begin by chopping the log into smaller pieces.  In the following example, I'm looking for a specific category on specific dates, one before the database migration and one after the database migration.
&lt;/p&gt;&lt;p&gt;&lt;pre class="brush: fsharp;"&gt;
let (_,premigration) = DateTime.TryParse("03/11/2011")
let (_,postmigration) = DateTime.TryParse("03/14/2011")

// Extraction engine to pull logs related to specific job @ specific date
let extract targetdate label file processor =
    parselog file logparser
    |&gt; List.filter (fun x -&gt; x.Timestamp.Date.Equals(targetdate))
    |&gt; List.filter (fun x -&gt; x.Category.Trim().Equals(label))
    |&gt; processor

// End log entry text to file
let send2file file entries=
    entries 
    |&gt; List.map (fun x -&gt; x.Entry)
    |&gt; List.concat
    |&gt; List.toArray
    |&gt; writefile file

// smaller working file - premigration for JOB A
(send2file @"JOBA.premigration.log") |&gt;
extract premigration.Date "JOBA" @"jobs.log" 

// smaller working file - postmigration for JOB A
(send2file @"JOBA.postmigration.log") |&gt;
extract postmigration.Date "JOBA" @"jobs.log" 
&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;Here's another example usage where I wanted to grab clusters of log entries along the time dimension (e.g. unique sets of Timestamp,Category pairs).&lt;/p&gt;&lt;p&gt;&lt;pre class="brush: fsharp;"&gt;
let getEntryClusters targetdate infile outfile  =
    parselog infile logparser
    |&gt; List.filter (fun x -&gt; x.Timestamp.Date.Equals(targetdate))
    |&gt; List.map (fun entry -&gt; (entry.Timestamp,entry.Category))
    |&gt; Set.ofList |&gt; Set.toList
    |&gt; List.map (fun (ts,category) -&gt; sprintf "%s,%s" (ts.ToString()) category)
    |&gt; List.toArray 
    |&gt; writefile outfile

getEntryClusters postmigration.Date @"jobs.log" @"clusters.log"
&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;The interactive capability of F# in Visual Studio made building this log analysis tool a lot easier than I expected.  While I can do similar interactive work with Ruby's irb, or Python's interactive mode, Visual Studio and F# just makes it feel much more productive than either of Ruby or Python's interactive environment.  If I had to analyze another log file with a different format, I can certainly reuse my code and maybe just changed the logparser implementation.  Indeed, just a few days after later, I was able to apply this code to look into some Tibco logs with minimal amount of changes.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/JohnLiaosBlog/~4/7Amee_pqz2Q" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://jyliao.blogspot.com/feeds/5510903789813899054/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=18281936&amp;postID=5510903789813899054" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/5510903789813899054?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18281936/posts/default/5510903789813899054?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JohnLiaosBlog/~3/7Amee_pqz2Q/log-analysis-with-f.html" title="Log Analysis with F#" /><author><name>John Liao</name><uri>http://www.blogger.com/profile/04740715435312568366</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://jyliao.blogspot.com/2011/03/log-analysis-with-f.html</feedburner:origLink></entry></feed>
