You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by zy...@apache.org on 2013/03/12 03:49:48 UTC

git commit: TS-1740: Improve precision of stats values

Updated Branches:
  refs/heads/master 9ddf58ba2 -> 5bcc19e6e


TS-1740: Improve precision of stats values

Conversion between types, such as RecInt(int64_t) to RecFloat(float),
may lead to loss of precision.

In the old code, token value was defined with StatFloat type, it would casue
unnecessary conversion when type of token is not StatFloat.

it's best to keep value with the same type of token as much as possiable.

Signed-off-by: Yunkai Zhang <qi...@taobao.com>
Signed-off-by: Zhao Yongming <mi...@gmail.com>


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/5bcc19e6
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/5bcc19e6
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/5bcc19e6

Branch: refs/heads/master
Commit: 5bcc19e6e901e7115bfbbcc6b4b8f60dfc51f4a7
Parents: 9ddf58b
Author: Yunkai Zhang <qi...@taobao.com>
Authored: Thu Feb 28 11:35:51 2013 +0800
Committer: Zhao Yongming <mi...@gmail.com>
Committed: Tue Mar 12 10:49:02 2013 +0800

----------------------------------------------------------------------
 CHANGES                     |    3 +
 lib/records/I_RecDefs.h     |    4 +-
 lib/records/P_RecUtils.h    |   10 +-
 lib/records/RecUtils.cc     |  199 ++++++++++++++++++++
 mgmt/stats/StatProcessor.cc |    4 +-
 mgmt/stats/StatProcessor.h  |    2 +-
 mgmt/stats/StatType.cc      |  374 ++++++++++++++++++++------------------
 mgmt/stats/StatType.h       |   58 +++---
 mgmt/utils/WebMgmtUtils.cc  |   43 +++++
 mgmt/utils/WebMgmtUtils.h   |    2 +
 mgmt/web2/WebOverview.cc    |   89 +++++++++-
 mgmt/web2/WebOverview.h     |    3 +
 12 files changed, 582 insertions(+), 209 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5bcc19e6/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index 35cc93d..1929eee 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,9 @@
                                                          -*- coding: utf-8 -*-
   Changes with Apache Traffic Server 3.3.2
   
+  *) [TS-1740] Improve precision of stats values
+   Author: Yunkai Zhang <qi...@taobao.com>
+
   *) [TS-1742] Freelists to use 64bit version w/ Double Word Compare and Swap
 
   *) [TS-1744] ats run with 1 less event thread then it is supposed to, for

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5bcc19e6/lib/records/I_RecDefs.h
----------------------------------------------------------------------
diff --git a/lib/records/I_RecDefs.h b/lib/records/I_RecDefs.h
index d19005e..bfacaa1 100644
--- a/lib/records/I_RecDefs.h
+++ b/lib/records/I_RecDefs.h
@@ -75,8 +75,8 @@ enum RecDataT
   RECD_COUNTER,
 
 #if defined(STAT_PROCESSOR)
-  RECD_STAT_CONST,              // Added for the StatProcessor
-  RECD_STAT_FX,                 // Added for the StatProcessor
+  RECD_CONST,               // Added for the StatProcessor, store as RECD_FLOAT
+  RECD_FX,                  // Added for the StatProcessor, store as RECD_INT
 #endif
   RECD_MAX
 };

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5bcc19e6/lib/records/P_RecUtils.h
----------------------------------------------------------------------
diff --git a/lib/records/P_RecUtils.h b/lib/records/P_RecUtils.h
index 28163ef..f8aecbe 100644
--- a/lib/records/P_RecUtils.h
+++ b/lib/records/P_RecUtils.h
@@ -50,11 +50,19 @@
 //-------------------------------------------------------------------------
 
 RecRecord *RecAlloc(RecT rec_type, const char *name, RecDataT data_type);
-void RecDataClear(RecDataT data_type, RecData * data);
+void RecDataClear(RecDataT type, RecData * data);
+void RecDataSetMax(RecDataT type, RecData * data);
+void RecDataSetMin(RecDataT type, RecData * data);
 bool RecDataSet(RecDataT data_type, RecData * data_dst, RecData * data_src);
 bool RecDataSetFromInk64(RecDataT data_type, RecData * data_dst, int64_t data_int64);
 bool RecDataSetFromFloat(RecDataT data_type, RecData * data_dst, float data_float);
 bool RecDataSetFromString(RecDataT data_type, RecData * data_dst, char *data_string);
+int RecDataCmp(RecDataT type, RecData left, RecData right);
+RecData RecDataAdd(RecDataT type, RecData left, RecData right);
+RecData RecDataSub(RecDataT type, RecData left, RecData right);
+RecData RecDataMul(RecDataT type, RecData left, RecData right);
+RecData RecDataDiv(RecDataT type, RecData left, RecData right);
+
 
 //-------------------------------------------------------------------------
 // Logging

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5bcc19e6/lib/records/RecUtils.cc
----------------------------------------------------------------------
diff --git a/lib/records/RecUtils.cc b/lib/records/RecUtils.cc
index a337efc..a3d47cd 100644
--- a/lib/records/RecUtils.cc
+++ b/lib/records/RecUtils.cc
@@ -67,6 +67,49 @@ RecDataClear(RecDataT data_type, RecData * data)
   memset(data, 0, sizeof(RecData));
 }
 
+void
+RecDataSetMax(RecDataT type, RecData * data)
+{
+  switch (type) {
+#if defined(STAT_PROCESSOR)
+  case RECD_FX:
+#endif
+  case RECD_INT:
+  case RECD_COUNTER:
+    data->rec_int = 0x7fffffffffffffff;
+    break;
+#if defined(STAT_PROCESSOR)
+  case RECD_CONST:
+#endif
+  case RECD_FLOAT:
+    data->rec_float = FLT_MAX;
+    break;
+  default:
+    Fatal("unsupport type:%d\n", type);
+  }
+}
+
+void
+RecDataSetMin(RecDataT type, RecData * data)
+{
+  switch (type) {
+#if defined(STAT_PROCESSOR)
+  case RECD_FX:
+#endif
+  case RECD_INT:
+  case RECD_COUNTER:
+    data->rec_int = 0x8000000000000000;
+    break;
+#if defined(STAT_PROCESSOR)
+  case RECD_CONST:
+#endif
+  case RECD_FLOAT:
+    data->rec_float = FLT_MIN;
+    break;
+  default:
+    Fatal("unsupport type:%d\n", type);
+  }
+}
 
 //-------------------------------------------------------------------------
 // RecDataSet
@@ -116,6 +159,144 @@ RecDataSet(RecDataT data_type, RecData * data_dst, RecData * data_src)
 
 }
 
+int
+RecDataCmp(RecDataT type, RecData left, RecData right)
+{
+  switch (type) {
+#if defined(STAT_PROCESSOR)
+  case RECD_FX:
+#endif
+  case RECD_INT:
+  case RECD_COUNTER:
+    if (left.rec_int > right.rec_int)
+      return 1;
+    else if (left.rec_int == right.rec_int)
+      return 0;
+    else
+      return -1;
+#if defined(STAT_PROCESSOR)
+  case RECD_CONST:
+#endif
+  case RECD_FLOAT:
+    if (left.rec_float > right.rec_float)
+      return 1;
+    else if (left.rec_float == right.rec_float)
+      return 0;
+    else
+      return -1;
+  default:
+    Fatal("unsupport type:%d\n", type);
+    return 0;
+  }
+}
+
+RecData
+RecDataAdd(RecDataT type, RecData left, RecData right)
+{
+  RecData val;
+  memset(&val, 0, sizeof(val));
+
+  switch (type) {
+#if defined(STAT_PROCESSOR)
+  case RECD_FX:
+#endif
+  case RECD_INT:
+  case RECD_COUNTER:
+    val.rec_int = left.rec_int + right.rec_int;
+    break;
+#if defined(STAT_PROCESSOR)
+  case RECD_CONST:
+#endif
+  case RECD_FLOAT:
+    val.rec_float = left.rec_float + right.rec_float;
+    break;
+  default:
+    Fatal("unsupported type:%d\n", type);
+    break;
+  }
+  return val;
+}
+
+RecData
+RecDataSub(RecDataT type, RecData left, RecData right)
+{
+  RecData val;
+  memset(&val, 0, sizeof(val));
+
+  switch (type) {
+#if defined(STAT_PROCESSOR)
+  case RECD_FX:
+#endif
+  case RECD_INT:
+  case RECD_COUNTER:
+    val.rec_int = left.rec_int - right.rec_int;
+    break;
+#if defined(STAT_PROCESSOR)
+  case RECD_CONST:
+#endif
+  case RECD_FLOAT:
+    val.rec_float = left.rec_float - right.rec_float;
+    break;
+  default:
+    Fatal("unsupported type:%d\n", type);
+    break;
+  }
+  return val;
+}
+
+RecData
+RecDataMul(RecDataT type, RecData left, RecData right)
+{
+  RecData val;
+  memset(&val, 0, sizeof(val));
+
+  switch (type) {
+#if defined(STAT_PROCESSOR)
+  case RECD_FX:
+#endif
+  case RECD_INT:
+  case RECD_COUNTER:
+    val.rec_int = left.rec_int * right.rec_int;
+    break;
+#if defined(STAT_PROCESSOR)
+  case RECD_CONST:
+#endif
+  case RECD_FLOAT:
+    val.rec_float = left.rec_float * right.rec_float;
+    break;
+  default:
+    Fatal("unsupported type:%d\n", type);
+    break;
+  }
+  return val;
+}
+
+RecData
+RecDataDiv(RecDataT type, RecData left, RecData right)
+{
+  RecData val;
+  memset(&val, 0, sizeof(val));
+
+  switch (type) {
+#if defined(STAT_PROCESSOR)
+  case RECD_FX:
+#endif
+  case RECD_INT:
+  case RECD_COUNTER:
+    val.rec_int = left.rec_int / right.rec_int;
+    break;
+#if defined(STAT_PROCESSOR)
+  case RECD_CONST:
+#endif
+  case RECD_FLOAT:
+    val.rec_float = left.rec_float / right.rec_float;
+    break;
+  default:
+    Fatal("unsupported type:%d\n", type);
+    break;
+  }
+  return val;
+}
 
 //-------------------------------------------------------------------------
 // RecDataSetFromInk64
@@ -124,9 +305,15 @@ bool
 RecDataSetFromInk64(RecDataT data_type, RecData * data_dst, int64_t data_int64)
 {
   switch (data_type) {
+#if defined(STAT_PROCESSOR)
+  case RECD_FX:
+#endif
   case RECD_INT:
     data_dst->rec_int = data_int64;
     break;
+#if defined(STAT_PROCESSOR)
+  case RECD_CONST:
+#endif
   case RECD_FLOAT:
     data_dst->rec_float = (float) (data_int64);
     break;
@@ -158,9 +345,15 @@ bool
 RecDataSetFromFloat(RecDataT data_type, RecData * data_dst, float data_float)
 {
   switch (data_type) {
+#if defined(STAT_PROCESSOR)
+  case RECD_FX:
+#endif
   case RECD_INT:
     data_dst->rec_int = (RecInt) data_float;
     break;
+#if defined(STAT_PROCESSOR)
+  case RECD_CONST:
+#endif
   case RECD_FLOAT:
     data_dst->rec_float = (float) (data_float);
     break;
@@ -195,9 +388,15 @@ RecDataSetFromString(RecDataT data_type, RecData * data_dst, char *data_string)
   RecData data_src;
 
   switch (data_type) {
+#if defined(STAT_PROCESSOR)
+  case RECD_FX:
+#endif
   case RECD_INT:
     data_src.rec_int = ink_atoi64(data_string);
     break;
+#if defined(STAT_PROCESSOR)
+  case RECD_CONST:
+#endif
   case RECD_FLOAT:
     data_src.rec_float = atof(data_string);
     break;

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5bcc19e6/mgmt/stats/StatProcessor.cc
----------------------------------------------------------------------
diff --git a/mgmt/stats/StatProcessor.cc b/mgmt/stats/StatProcessor.cc
index a96c033..98ce28d 100644
--- a/mgmt/stats/StatProcessor.cc
+++ b/mgmt/stats/StatProcessor.cc
@@ -73,8 +73,10 @@ startElement(void *userData, const char *name, const char **atts)
 
       if (!strcmp(atts[i], "minimum")) {
         statObject->m_stats_min = (MgmtFloat) atof(atts[i + 1]);
+        statObject->m_has_min = true;
       } else if (!strcmp(atts[i], "maximum")) {
         statObject->m_stats_max = (MgmtFloat) atof(atts[i + 1]);
+        statObject->m_has_max = true;
       } else if (!strcmp(atts[i], "interval")) {
         statObject->m_update_interval = (ink_hrtime) atoi(atts[i + 1]);
       } else if (!strcmp(atts[i], "debug")) {
@@ -307,7 +309,7 @@ StatProcessor::processStat()
  * --------------
  *
  */
-MgmtFloat
+RecData
 ExpressionEval(char *exprString)
 {
   StatObject statObject;

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5bcc19e6/mgmt/stats/StatProcessor.h
----------------------------------------------------------------------
diff --git a/mgmt/stats/StatProcessor.h b/mgmt/stats/StatProcessor.h
index c400188..8e6b0c6 100644
--- a/mgmt/stats/StatProcessor.h
+++ b/mgmt/stats/StatProcessor.h
@@ -78,6 +78,6 @@ public:
  *
  */
 
-MgmtFloat ExpressionEval(char *);
+RecData ExpressionEval(char *);
 
 #endif

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5bcc19e6/mgmt/stats/StatType.cc
----------------------------------------------------------------------
diff --git a/mgmt/stats/StatType.cc b/mgmt/stats/StatType.cc
index 220275c..e206272 100644
--- a/mgmt/stats/StatType.cc
+++ b/mgmt/stats/StatType.cc
@@ -45,11 +45,13 @@ bool StatDebug = false;         // global debug flag
 StatExprToken::StatExprToken()
   : m_arith_symbol('\0'),
     m_token_name(NULL),
-    m_token_type((StatDataT)RECD_NULL),
-    m_token_value(0.0),
-    m_token_value_max(FLT_MAX),
-    m_token_value_min(-1 * FLT_MAX), m_token_value_delta(NULL), m_sum_var(false), m_node_var(true)
+    m_token_type(RECD_NULL),
+    m_sum_var(false), m_node_var(true)
 {
+  RecDataClear(RECD_NULL, &m_token_value);
+  RecDataClear(RECD_NULL, &m_token_value_max);
+  RecDataClear(RECD_NULL, &m_token_value_min);
+  memset(&m_token_value_delta, 0, sizeof(m_token_value_delta));
 }
 
 
@@ -72,7 +74,7 @@ StatExprToken::copy(const StatExprToken & source)
   m_token_value_max = source.m_token_value_max;
 
   if (source.m_token_value_delta) {
-    m_token_value_delta = NEW(new StatFloatSamples());
+    m_token_value_delta = NEW(new StatDataSamples());
     m_token_value_delta->previous_time = source.m_token_value_delta->previous_time;
     m_token_value_delta->current_time = source.m_token_value_delta->current_time;
     m_token_value_delta->previous_value = source.m_token_value_delta->previous_value;
@@ -95,76 +97,42 @@ StatExprToken::assignTokenName(const char *name)
 {
 
   if (isdigit(name[0])) {
-
     // numerical constant
     m_token_name = ats_strdup("CONSTANT");
-    m_token_type = STAT_CONST;
-
+    m_token_type = RECD_CONST;
   } else {
-
     m_token_name = ats_strdup(name);
     assignTokenType();
   }
 
   switch (m_token_type) {
-  case STAT_INT:
-    if (StatDebug) {
-      StatInt tempInt;
-      if (!varIntFromName(m_token_name, &tempInt)) {
-        tempInt = (RecInt) ERROR_VALUE;
-      }
-      Debug(MODULE_INIT, "\tvar: %s, type: %d, value: %" PRId64 "\n", m_token_name, m_token_type, tempInt);
-    }
-    break;
-
-  case STAT_FLOAT:
-    if (StatDebug) {
-      StatFloat tempFloat;
-      if (!varFloatFromName(m_token_name, &tempFloat)) {
-        tempFloat = (RecFloat) ERROR_VALUE;
-      }
-      Debug(MODULE_INIT, "\tvar: %s, type: %d, value: %f\n", m_token_name, m_token_type, tempFloat);
-    }
+  case RECD_INT:
+  case RECD_COUNTER:
+  case RECD_FLOAT:
     break;
-
-  case STAT_CONST:
+  case RECD_CONST:
     // assign pre-defined constant in here
+    // constant will be stored as RecFloat type.
     if (!strcmp(m_token_name, "CONSTANT")) {
-      m_token_value = (StatFloat) atof(name);
+      m_token_value.rec_float = (RecFloat) atof(name);
     } else if (!strcmp(m_token_name, "$BYTES_TO_MB_SCALE")) {
-      m_token_value = (StatFloat) BYTES_TO_MB_SCALE;
+      m_token_value.rec_float = (RecFloat) BYTES_TO_MB_SCALE;
     } else if (!strcmp(m_token_name, "$MBIT_TO_KBIT_SCALE")) {
-      m_token_value = (StatFloat) MBIT_TO_KBIT_SCALE;
+      m_token_value.rec_float = (RecFloat) MBIT_TO_KBIT_SCALE;
     } else if (!strcmp(m_token_name, "$SECOND_TO_MILLISECOND_SCALE")) {
-      m_token_value = (StatFloat) SECOND_TO_MILLISECOND_SCALE;
+      m_token_value.rec_float = (RecFloat) SECOND_TO_MILLISECOND_SCALE;
     } else if (!strcmp(m_token_name, "$PCT_TO_INTPCT_SCALE")) {
-      m_token_value = (StatFloat) PCT_TO_INTPCT_SCALE;
+      m_token_value.rec_float = (RecFloat) PCT_TO_INTPCT_SCALE;
     } else if (!strcmp(m_token_name, "$HRTIME_SECOND")) {
-      m_token_value = (StatFloat) HRTIME_SECOND;
+      m_token_value.rec_float = (RecFloat) HRTIME_SECOND;
     } else if (!strcmp(m_token_name, "$BYTES_TO_MBIT_SCALE")) {
-      m_token_value = (StatFloat) BYTES_TO_MBIT_SCALE;
+      m_token_value.rec_float = (RecFloat) BYTES_TO_MBIT_SCALE;
     } else {
       mgmt_log(stderr, "[StatPro] ERROR: Undefined constant: %s\n", m_token_name);
       StatError = true;
     }
-
-    if (StatDebug) {
-      Debug(MODULE_INIT, "\tconst: %s, type: %d, value: %f\n", m_token_name, m_token_type, m_token_value);
-    }
-    break;
-
-  case STAT_FX:
-    if (StatDebug) {
-      Debug(MODULE_INIT, "\tfunction: %s, type: %d\n", m_token_name, m_token_type);
-    }
-    break;
-
+  case RECD_FX:
   default:
-    /*
-       mgmt_log(stderr, "[StatPro] ERROR: Undefined token: %s\n",
-       m_token_name);
-       StatError = true;
-     */
     break;
   }
 }
@@ -180,20 +148,25 @@ StatExprToken::assignTokenName(const char *name)
 bool StatExprToken::assignTokenType()
 {
   ink_debug_assert(m_token_name != NULL);
-  m_token_type = (StatDataT)varType(m_token_name);
+  m_token_type = varType(m_token_name);
 
   if (m_token_name[0] == '$') {
-    m_token_type = STAT_CONST;
+    m_token_type = RECD_CONST;
   } else if (m_token_name[0] == '_') {
-    m_token_type = STAT_FX;
+    m_token_type = RECD_FX;
+  }
+
+  if (m_token_type == RECD_COUNTER) {
+    m_token_type = RECD_INT;
   }
 
-  if (m_token_type == STAT_COUNTER) {
-    m_token_type = STAT_INT;
+  if (m_token_value_delta) {
+    m_token_value_delta->data_type = m_token_type;
   }
+
   // I'm guessing here that we want to check if we're still RECD_NULL,
   // it used to be INVALID, which is not in the m_token_type's enum. /leif
-  return (m_token_type != (StatDataT)RECD_NULL);
+  return (m_token_type != RECD_NULL);
 }
 
 
@@ -255,31 +228,26 @@ StatExprToken::precedence()
  * or larger than max, then the error value is assigned. If no
  * error value is assigned, either min. or max. is assigned.
  */
-bool StatExprToken::statVarSet(StatFloat value)
+bool StatExprToken::statVarSet(RecData value)
 {
   if (StatError) {
     /* fix this after librecords is done
        mgmt_log(stderr,
        "[StatPro] ERROR in a statistics aggregation operations\n");
      */
-    return varSetFloat(m_token_name, ERROR_VALUE, true);
+    RecData err_value;
+    RecDataClear(m_token_type, &err_value);
+    return varSetData(m_token_type, m_token_name, err_value);
   }
 
-  if (value < m_token_value_min) {
-    if (StatDebug) {
-      Debug(MODULE, "[StatPro] Reset min. value: %f < %f\n", value, m_token_value_min);
-    }
+  if (RecDataCmp(m_token_type, value, m_token_value_min) < 0) {
     value = m_token_value_min;
   }
-
-  if (value > m_token_value_max) {
-    if (StatDebug) {
-      Debug(MODULE, "[StatPro] Reset max. value: %f > %f\n", value, m_token_value_max);
-    }
+  else if (RecDataCmp(m_token_type, value, m_token_value_max) > 0) {
     value = m_token_value_max;
   }
 
-  return varSetFloat(m_token_name, value, true);
+  return varSetData(m_token_type, m_token_name, value);
 }
 
 
@@ -429,7 +397,9 @@ StatObject::StatObject()
    m_expression(NULL),
    m_postfix(NULL),
    m_last_update(-1),
-   m_current_time(-1), m_update_interval(-1), m_stats_max(FLT_MAX), m_stats_min(-1 * FLT_MAX), m_has_delta(false)
+   m_current_time(-1), m_update_interval(-1),
+   m_stats_max(FLT_MAX), m_stats_min(FLT_MIN),
+   m_has_max(false), m_has_min(false), m_has_delta(false)
 {
 }
 
@@ -443,7 +413,9 @@ StatObject::StatObject(unsigned identifier)
     m_expression(NULL),
     m_postfix(NULL),
     m_last_update(-1),
-    m_current_time(-1), m_update_interval(-1), m_stats_max(FLT_MAX), m_stats_min(-1 * FLT_MAX), m_has_delta(false)
+    m_current_time(-1), m_update_interval(-1),
+    m_stats_max(FLT_MAX), m_stats_min(FLT_MIN),
+    m_has_max(false), m_has_min(false), m_has_delta(false)
 {
 }
 
@@ -479,6 +451,24 @@ StatObject::assignDst(const char *str, bool m_node_var, bool m_sum_var)
   statToken->m_node_var = m_node_var;
   statToken->m_sum_var = m_sum_var;
 
+  // The type of dst token should be always not NULL
+  if (statToken->m_token_type == RECD_NULL) {
+    Fatal("token:%s, invalid token type!", statToken->m_token_name);
+  }
+
+  // Set max/min value
+  if (m_has_max)
+    RecDataSetFromFloat(statToken->m_token_type, &statToken->m_token_value_max,
+                        m_stats_max);
+  else
+    RecDataSetMax(statToken->m_token_type, &statToken->m_token_value_max);
+
+  if (m_has_min)
+    RecDataSetFromFloat(statToken->m_token_type, &statToken->m_token_value_min,
+                        m_stats_min);
+  else
+    RecDataSetMin(statToken->m_token_type, &statToken->m_token_value_min);
+
   if (m_node_var) {
     ink_assert(m_node_dest == NULL);
     m_node_dest = statToken;
@@ -534,11 +524,12 @@ StatObject::assignExpr(char *str)
       if (token[0] == '#') {
 
         token += 1;             // skip '#'
-        statToken->m_token_value_delta = NEW(new StatFloatSamples());
+        statToken->m_token_value_delta = NEW(new StatDataSamples());
         statToken->m_token_value_delta->previous_time = (ink_hrtime) 0;
         statToken->m_token_value_delta->current_time = (ink_hrtime) 0;
-        statToken->m_token_value_delta->previous_value = (StatFloat) 0.0;
-        statToken->m_token_value_delta->current_value = (StatFloat) 0.0;
+        statToken->m_token_value_delta->data_type = RECD_NULL;
+        RecDataClear(RECD_NULL, &statToken->m_token_value_delta->previous_value);
+        RecDataClear(RECD_NULL, &statToken->m_token_value_delta->current_value);
 
       }
 
@@ -641,43 +632,41 @@ StatObject::infix2postfix()
  *
  *
  */
-StatFloat StatObject::NodeStatEval(bool cluster)
+RecData StatObject::NodeStatEval(bool cluster)
 {
   StatExprList stack;
   StatExprToken *left = NULL;
   StatExprToken *right = NULL;
   StatExprToken *result = NULL;
   StatExprToken *curToken = NULL;
-  StatFloat tempValue = ERROR_VALUE;
+  RecData tempValue;
+  RecDataClear(RECD_NULL, &tempValue);
 
   /* Express checkout lane -- Stat. object with on 1 source variable */
   if (m_postfix->count() == 1) {
-    StatExprToken *
-      src = m_postfix->top();
+    StatExprToken * src = m_postfix->top();
 
     // in librecords, not all statistics are register at initialization
     // must assign proper type if it is undefined.
-    if (src->m_token_type == (StatDataT)RECD_NULL) {
+    if (src->m_token_type == RECD_NULL) {
       src->assignTokenType();
     }
 
-    if (src->m_token_type == STAT_CONST) {
+    if (src->m_token_type == RECD_CONST) {
       tempValue = src->m_token_value;
     } else if (src->m_token_value_delta) {
-      tempValue = src->m_token_value_delta->diff_value();;
+      tempValue = src->m_token_value_delta->diff_value(src->m_token_name);
     } else if (!cluster) {
-      if (!varFloatFromName(src->m_token_name, &tempValue)) {
-        tempValue = (RecFloat) ERROR_VALUE;
+      if (!varDataFromName(src->m_token_type, src->m_token_name, &tempValue)) {
+        RecDataClear(RECD_NULL, &tempValue);
       }
     } else {
-      if (!overviewGenerator->varClusterFloatFromName(src->m_token_name, &tempValue)) {
-        tempValue = (RecFloat) ERROR_VALUE;
+      if (!overviewGenerator->varClusterDataFromName(src->m_token_type,
+                                                     src->m_token_name,
+                                                     &tempValue)) {
+        RecDataClear(RECD_NULL, &tempValue);
       }
     }
-
-    if (StatDebug) {
-      Debug(MODULE, "\tExpress checkout : %s:%f\n", src->m_token_name, tempValue);
-    }
   } else {
 
     /* standard postfix evaluation */
@@ -693,10 +682,10 @@ StatFloat StatObject::NodeStatEval(bool cluster)
         right = stack.pop();
         left = stack.pop();
 
-        if (left->m_token_type == (StatDataT)RECD_NULL) {
+        if (left->m_token_type == RECD_NULL) {
           left->assignTokenType();
         }
-        if (right->m_token_type == (StatDataT)RECD_NULL) {
+        if (right->m_token_type == RECD_NULL) {
           right->assignTokenType();
         }
 
@@ -718,7 +707,7 @@ StatFloat StatObject::NodeStatEval(bool cluster)
     tempValue = stack.top()->m_token_value;
   }
 
-  return (tempValue);
+  return tempValue;
 
 }
 
@@ -729,9 +718,9 @@ StatFloat StatObject::NodeStatEval(bool cluster)
  *
  *
  */
-StatFloat StatObject::ClusterStatEval()
+RecData StatObject::ClusterStatEval()
 {
-  StatFloat tempValue = ERROR_VALUE;
+  RecData tempValue;
 
   /* Sanity check */
   ink_debug_assert(m_cluster_dest && !m_cluster_dest->m_node_var);
@@ -740,11 +729,10 @@ StatFloat StatObject::ClusterStatEval()
   if ((m_node_dest == NULL) || (m_cluster_dest->m_sum_var == false)) {
     return NodeStatEval(true);
   } else {
-    if (!overviewGenerator->varClusterFloatFromName(m_node_dest->m_token_name, &tempValue)) {
-      tempValue = (RecFloat) ERROR_VALUE;
-    }
-    if (StatDebug) {
-      Debug(MODULE, "Exp. chkout write: %s:%f\n", m_node_dest->m_token_name, tempValue);
+    if (!overviewGenerator->varClusterDataFromName(m_node_dest->m_token_type,
+                                                   m_node_dest->m_token_name,
+                                                   &tempValue)) {
+      RecDataClear(RECD_NULL, &tempValue);
     }
     return (tempValue);
   }
@@ -757,9 +745,9 @@ StatFloat StatObject::ClusterStatEval()
  * The logic of the following code segment is the following.
  * The objective is to extract the appropriate right->m_token_value.
  * If 'right' is an intermediate value, nothing to do.
- * If m_token_type is STAT_CONST, nothing to do.
- * If m_token_type is STAT_FX, right->m_token_value is the diff. in time.
- * If m_token_type is either STAT_INT or STAT_FLOAT, it can either
+ * If m_token_type is RECD_CONST, nothing to do.
+ * If m_token_type is RECD_FX, right->m_token_value is the diff. in time.
+ * If m_token_type is either RECD_INT or RECD_FLOAT, it can either
  * by a cluster variable or a node variable.
  *     If it is a cluster variable, just use varClusterFloatFromName
  *     to set right->m_token_value.
@@ -776,36 +764,32 @@ StatObject::setTokenValue(StatExprToken * token, bool cluster)
     // it is NOT an intermediate value
 
     switch (token->m_token_type) {
-    case STAT_CONST:
+    case RECD_CONST:
       break;
 
-    case STAT_FX:
+    case RECD_FX:
       // only support time function
-      token->m_token_value = (StatFloat) (m_current_time - m_last_update);
-      if (StatDebug) {
-        Debug(MODULE, "m_current_time(%"  PRId64 ") - m_last_update(%"  PRId64 ") = %"  PRId64 "\n",
-              (int64_t)m_current_time, (int64_t)m_last_update, (int64_t)token->m_token_value);
-      }
+      // use rec_int to store time value
+      token->m_token_value.rec_int = (m_current_time - m_last_update);
       break;
 
-    case STAT_INT:             // fallthought
-    case STAT_FLOAT:
+    case RECD_INT:             // fallthought
+    case RECD_FLOAT:
       if (cluster) {
-        if (!overviewGenerator->varClusterFloatFromName(token->m_token_name, &(token->m_token_value))) {
-          token->m_token_value = (RecFloat) ERROR_VALUE;
+        if (!overviewGenerator->varClusterDataFromName(token->m_token_type,
+                                                       token->m_token_name,
+                                                       &(token->m_token_value)))
+        {
+          RecDataClear(token->m_token_type, &token->m_token_value);
         }
       } else {
         if (token->m_token_value_delta) {
-          token->m_token_value = token->m_token_value_delta->diff_value();
-
-          if (StatDebug) {
-            Debug(MODULE, "\tDelta value: %f %f %f\n",
-                  token->m_token_value_delta->previous_value,
-                  token->m_token_value_delta->current_value, token->m_token_value);
-          }
+          token->m_token_value =
+            token->m_token_value_delta->diff_value(token->m_token_name);
         } else {
-          if (!varFloatFromName(token->m_token_name, &(token->m_token_value))) {
-            token->m_token_value = (RecFloat) ERROR_VALUE;
+          if (!varDataFromName(token->m_token_type, token->m_token_name,
+                               &(token->m_token_value))) {
+            RecDataClear(token->m_token_type, &token->m_token_value);
           }
         }                       // delta?
       }                         // cluster?
@@ -813,7 +797,8 @@ StatObject::setTokenValue(StatExprToken * token, bool cluster)
 
     default:
       if (StatDebug) {
-        Debug(MODULE, "Unrecognized token \"%s\" of type %d.\n", token->m_token_name, token->m_token_type);
+        Debug(MODULE, "Unrecognized token \"%s\" of type %d.\n",
+              token->m_token_name, token->m_token_type);
       }
     }                           // switch
   }                             // m_token_name?
@@ -831,31 +816,103 @@ StatObject::setTokenValue(StatExprToken * token, bool cluster)
  * - (3) cluster variable
  * - (4) an immediate value
  */
-
-StatExprToken *StatObject::StatBinaryEval(StatExprToken * left, char op, StatExprToken * right, bool cluster)
+StatExprToken *StatObject::StatBinaryEval(StatExprToken * left, char op,
+                                          StatExprToken * right, bool cluster)
 {
+  RecData l, r;
   StatExprToken *result = NEW(new StatExprToken());
-  result->m_token_type = STAT_FLOAT;
+  result->m_token_type = RECD_INT;
 
-  setTokenValue(left, cluster);
-  setTokenValue(right, cluster);
+  if (left->m_token_type == RECD_NULL
+      && right->m_token_type == RECD_NULL) {
+    Warning("Both two token type are RECD_NULL.");
+    return result;
+  }
 
+  if (left->m_token_type != RECD_NULL) {
+    setTokenValue(left, cluster);
+    result->m_token_type = left->m_token_type;
+  }
+
+  if (right->m_token_type != RECD_NULL) {
+    setTokenValue(right, cluster);
+    switch (result->m_token_type) {
+    case RECD_NULL:
+      result->m_token_type = right->m_token_type;
+      break;
+    case RECD_FX:
+    case RECD_INT:
+    case RECD_COUNTER:
+      /*
+       * When types of left and right are different, select RECD_FLOAT
+       * as result type. It's may lead to loss of precision when do
+       * conversion, be careful!
+       */
+      if (right->m_token_type == RECD_FLOAT
+          || right->m_token_type == RECD_CONST) {
+        result->m_token_type = right->m_token_type;
+      }
+      break;
+    case RECD_CONST:
+    case RECD_FLOAT:
+      break;
+    default:
+      Fatal("Unexpected RecData Type:%d", result->m_token_type);
+      break;
+    }
+  }
+
+  /*
+   * We should make the operands with the same type before calculating.
+   */
+  RecDataClear(RECD_NULL, &l);
+  RecDataClear(RECD_NULL, &r);
+
+  if (left->m_token_type == right->m_token_type ) {
+    l = left->m_token_value;
+    r = right->m_token_value;
+  } else if (result->m_token_type != left->m_token_type) {
+    if (left->m_token_type != RECD_NULL) {
+      ink_assert(result->m_token_type == RECD_FLOAT
+                 || result->m_token_type == RECD_CONST);
+
+      l.rec_float = (RecFloat)left->m_token_value.rec_int;
+    }
+    r = right->m_token_value;
+    ink_assert(result->m_token_type == right->m_token_type);
+  } else {
+    l = left->m_token_value;
+    if (right->m_token_type != RECD_NULL) {
+      ink_assert(result->m_token_type == RECD_FLOAT
+                 || result->m_token_type == RECD_CONST);
+
+      r.rec_float = (RecFloat)right->m_token_value.rec_int;
+    }
+    ink_assert(result->m_token_type == left->m_token_type);
+  }
+
+  /*
+   * Start to calculate
+   */
   switch (op) {
   case '+':
-    result->m_token_value = (StatFloat) (left->m_token_value + right->m_token_value);
+    result->m_token_value = RecDataAdd(result->m_token_type, l, r);
     break;
 
   case '-':
-    result->m_token_value = (StatFloat) (left->m_token_value - right->m_token_value);
+    result->m_token_value = RecDataSub(result->m_token_type, l, r);
     break;
 
   case '*':
-    result->m_token_value = (StatFloat) (left->m_token_value * right->m_token_value);
+    result->m_token_value = RecDataMul(result->m_token_type, l, r);
     break;
 
   case '/':
-    result->m_token_value = (StatFloat) ((right->m_token_value == 0) ?
-                                         0.0 : (left->m_token_value / right->m_token_value));
+    RecData recTmp;
+    RecDataClear(RECD_NULL, &recTmp);
+    if (RecDataCmp(result->m_token_type, r, recTmp)) {
+      result->m_token_value = RecDataDiv(result->m_token_type, l, r);
+    }
     break;
 
   default:
@@ -863,13 +920,6 @@ StatExprToken *StatObject::StatBinaryEval(StatExprToken * left, char op, StatExp
     StatError = true;
   }
 
-  if (StatDebug) {
-    Debug(MODULE, "%s(%f) %c %s(%f) = %f\n",
-          left->m_token_name ? left->m_token_name : "in stack",
-          left->m_token_value,
-          op, right->m_token_name ? right->m_token_name : "in stack", right->m_token_value, result->m_token_value);
-  }
-
   return (result);
 }
 
@@ -908,16 +958,6 @@ StatObjectList::enqueue(StatObject * object)
     }
   }
 
-  if (object->m_node_dest) {
-    object->m_node_dest->m_token_value_max = object->m_stats_max;
-    object->m_node_dest->m_token_value_min = object->m_stats_min;
-  }
-
-  if (object->m_cluster_dest) {
-    object->m_cluster_dest->m_token_value_max = object->m_stats_max;
-    object->m_cluster_dest->m_token_value_min = object->m_stats_min;
-  }
-
   m_statList.enqueue(object);
   m_size += 1;
 }
@@ -945,12 +985,15 @@ StatObjectList::next(StatObject * current)
 short
 StatObjectList::Eval()
 {
-  StatFloat tempValue = ERROR_VALUE;
-  StatFloat result = ERROR_VALUE;
+  RecData tempValue;
+  RecData result;
   ink_hrtime threshold = 0;
   ink_hrtime delta = 0;
   short count = 0;
 
+  RecDataClear(RECD_NULL, &tempValue);
+  RecDataClear(RECD_NULL, &result);
+
   for (StatObject * object = first(); object; object = next(object)) {
     StatError = false;
     StatDebug = object->m_debug;
@@ -966,17 +1009,11 @@ StatObjectList::Eval()
       if (object->m_node_dest) {
         result = object->NodeStatEval(false);
         object->m_node_dest->statVarSet(result);
-        if (StatDebug) {
-          Debug(MODULE, "\t==>Result: %s -> %f\n", object->m_node_dest->m_token_name, result);
-        }
       }
 
       if (object->m_cluster_dest) {
         result = object->ClusterStatEval();
         object->m_cluster_dest->statVarSet(result);
-        if (StatDebug) {
-          Debug(MODULE, "\t==>Result: %s -> %f\n", object->m_cluster_dest->m_token_name, result);
-        }
       }
 
       object->m_last_update = object->m_current_time;
@@ -1017,17 +1054,11 @@ StatObjectList::Eval()
           if (object->m_node_dest) {
             result = object->NodeStatEval(false);
             object->m_node_dest->statVarSet(result);
-            if (StatDebug) {
-              Debug(MODULE, "\t==>Result: %s -> %f\n", object->m_node_dest->m_token_name, result);
-            }
           }
 
           if (object->m_cluster_dest) {
             result = object->ClusterStatEval();
             object->m_cluster_dest->statVarSet(result);
-            if (StatDebug) {
-              Debug(MODULE, "\t==>Result: %s -> %f\n", object->m_cluster_dest->m_token_name, result);
-            }
           }
 
           object->m_last_update = object->m_current_time;
@@ -1039,8 +1070,9 @@ StatObjectList::Eval()
           // scroll old values
           for (StatExprToken * token = object->m_postfix->first(); token; token = object->m_expression->next(token)) {
             if (token->m_token_value_delta) {
-              if (!varFloatFromName(token->m_token_name, &tempValue)) {
-                tempValue = (RecFloat) ERROR_VALUE;
+              if (!varDataFromName(token->m_token_type, token->m_token_name,
+                                   &tempValue)) {
+                RecDataClear(RECD_NULL, &tempValue);
               }
 
               token->m_token_value_delta->previous_time = token->m_token_value_delta->current_time;
@@ -1054,17 +1086,11 @@ StatObjectList::Eval()
             if (object->m_node_dest) {
               result = object->NodeStatEval(false);
               object->m_node_dest->statVarSet(result);
-              if (StatDebug) {
-                Debug(MODULE, "\t==>Result: %s -> %f\n", object->m_node_dest->m_token_name, result);
-              }
             }
 
             if (object->m_cluster_dest) {
               result = object->ClusterStatEval();
               object->m_cluster_dest->statVarSet(result);
-              if (StatDebug) {
-                Debug(MODULE, "\t==>Result: %s -> %f\n", object->m_cluster_dest->m_token_name, result);
-              }
             }
 
             object->m_last_update = object->m_current_time;

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5bcc19e6/mgmt/stats/StatType.h
----------------------------------------------------------------------
diff --git a/mgmt/stats/StatType.h b/mgmt/stats/StatType.h
index fd31793..3d56ee3 100644
--- a/mgmt/stats/StatType.h
+++ b/mgmt/stats/StatType.h
@@ -39,31 +39,29 @@
 
 #define BYTES_TO_MBIT_SCALE (8/1000000.0)
 
-#define ERROR_VALUE      0
-#define StatDataT        RecDataT
-#define StatFloat        RecFloat
-#define StatInt          RecInt
-#define StatCounter      RecCounter
-#define StatString       RecString
-
-#define STAT_INT         RECD_INT
-#define STAT_FLOAT       RECD_FLOAT
-#define STAT_STRING      RECD_STRING
-#define STAT_COUNTER     RECD_COUNTER
-#define STAT_CONST       RECD_STAT_CONST
-#define STAT_FX          RECD_STAT_FX
-
 /* Structs used in Average Statistics calculations */
-struct StatFloatSamples
+struct StatDataSamples
 {
   ink_hrtime previous_time;
   ink_hrtime current_time;
-  StatFloat previous_value;
-  StatFloat current_value;
+  RecDataT data_type;
+  RecData previous_value;
+  RecData current_value;
 
-  StatFloat diff_value()
+  RecData diff_value(const char *name)
   {
-    return (current_value - previous_value);
+    RecData tmp;
+
+    if (data_type == RECD_NULL) {
+      data_type = varType(name);
+    }
+
+    if (data_type != RECD_NULL)
+      return RecDataSub(data_type, current_value, previous_value);
+    else {
+      RecDataClear(RECD_NULL, &tmp);
+      return tmp;
+    }
   }
   ink_hrtime diff_time()
   {
@@ -93,11 +91,11 @@ public:
 
   char m_arith_symbol;
   char *m_token_name;
-  StatDataT m_token_type;
-  StatFloat m_token_value;
-  StatFloat m_token_value_max;
-  StatFloat m_token_value_min;
-  StatFloatSamples *m_token_value_delta;
+  RecDataT m_token_type;
+  RecData m_token_value;
+  RecData m_token_value_max;
+  RecData m_token_value_min;
+  StatDataSamples *m_token_value_delta;
   bool m_sum_var;
   bool m_node_var;
 
@@ -116,7 +114,7 @@ public:
   };
   void clean();
 
-  bool statVarSet(StatFloat);
+  bool statVarSet(RecData);
 };
 
 
@@ -172,8 +170,10 @@ public:
   ink_hrtime m_last_update;
   ink_hrtime m_current_time;
   ink_hrtime m_update_interval;
-  StatFloat m_stats_max;
-  StatFloat m_stats_min;
+  RecFloat m_stats_max;
+  RecFloat m_stats_min;
+  bool m_has_max;
+  bool m_has_min;
   bool m_has_delta;
   LINK(StatObject, link);
 
@@ -189,8 +189,8 @@ public:
   void assignExpr(char *);
 
   StatExprToken *StatBinaryEval(StatExprToken *, char, StatExprToken *, bool cluster = false);
-  StatFloat NodeStatEval(bool cluster);
-  StatFloat ClusterStatEval();
+  RecData NodeStatEval(bool cluster);
+  RecData ClusterStatEval();
   void setTokenValue(StatExprToken *, bool cluster = false);
 
 private:

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5bcc19e6/mgmt/utils/WebMgmtUtils.cc
----------------------------------------------------------------------
diff --git a/mgmt/utils/WebMgmtUtils.cc b/mgmt/utils/WebMgmtUtils.cc
index 8118fca..9d292ef 100644
--- a/mgmt/utils/WebMgmtUtils.cc
+++ b/mgmt/utils/WebMgmtUtils.cc
@@ -248,6 +248,49 @@ varSetInt(const char *varName, RecInt value, bool convert)
   return found;
 }
 
+// bool varSetData(RecDataT varType, const char *varName, RecData value)
+//
+//  Sets the variable specifed by varName to value. value and varName
+//   must be varType variables.
+//
+bool
+varSetData(RecDataT varType, const char *varName, RecData value)
+{
+  int err = REC_ERR_FAIL;
+
+  switch (varType) {
+  case RECD_INT:
+    err = RecSetRecordInt((char *)varName, value.rec_int);
+    break;
+  case RECD_COUNTER:
+    err = RecSetRecordCounter((char *)varName, value.rec_counter);
+    break;
+  case RECD_FLOAT:
+    err = RecSetRecordFloat((char *)varName, value.rec_float);
+    break;
+  default:
+    Fatal("unsupport type:%d\n", varType);
+  }
+  return (err == REC_ERR_OKAY);
+}
+
+// bool varDataFromName(RecDataT varType, const char *varName, RecData *value)
+//
+//   Sets the *value to value of the varName according varType.
+//
+//  return true if bufVal was succefully set
+//    and false otherwise
+//
+bool
+varDataFromName(RecDataT varType, const char *varName, RecData *value)
+{
+  int err;
+
+  err = RecGetRecord_Xmalloc(varName, varType, value, true);
+
+  return (err == REC_ERR_OKAY);
+}
+
 
 // bool varCounterFromName (const char*, RecFloat* )
 //

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5bcc19e6/mgmt/utils/WebMgmtUtils.h
----------------------------------------------------------------------
diff --git a/mgmt/utils/WebMgmtUtils.h b/mgmt/utils/WebMgmtUtils.h
index aad4564..1f4630c 100644
--- a/mgmt/utils/WebMgmtUtils.h
+++ b/mgmt/utils/WebMgmtUtils.h
@@ -77,6 +77,7 @@ bool varSetFromStr(const char *varName, const char *value);
 bool varIntFromName(const char *varName, RecInt * value);
 bool varFloatFromName(const char *varName, RecFloat * value);
 bool varCounterFromName(const char *varName, RecCounter * value);
+bool varDataFromName(RecDataT varType, const char *varName, RecData *value);
 
 // No conversion done.  varName must represnt a value of the appropriate
 //  type
@@ -84,6 +85,7 @@ bool varCounterFromName(const char *varName, RecCounter * value);
 bool varSetInt(const char *varName, RecInt value, bool convert = false);
 bool varSetCounter(const char *varName, RecCounter value, bool convert = false);
 bool varSetFloat(const char *varName, RecFloat value, bool convert = false);
+bool varSetData(RecDataT varType, const char *varName, RecData value);
 
 // Return the type of the variable named
 RecDataT varType(const char *varName);

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5bcc19e6/mgmt/web2/WebOverview.cc
----------------------------------------------------------------------
diff --git a/mgmt/web2/WebOverview.cc b/mgmt/web2/WebOverview.cc
index fc380fe..af7cd10 100644
--- a/mgmt/web2/WebOverview.cc
+++ b/mgmt/web2/WebOverview.cc
@@ -258,7 +258,6 @@ overviewRecord::checkAlarms()
 }
 
 //  overview::readCounter, overview::readInteger
-//  overview::readFloat, overview::readString
 //
 //  Accessor functions for node records.  For remote node,
 //    we get the value in the node_data array we maintain
@@ -373,6 +372,46 @@ overviewRecord::readString(const char *name, bool * found)
   return rec;
 }
 
+//  overview::readData, read RecData according varType
+//
+//  Accessor functions for node records.  For remote node,
+//    we get the value in the node_data array we maintain
+//    in this object.  For the node, we do not maintain any data
+//    and rely on lmgmt->record_data for both the retrieval
+//    code and the records array
+//
+//  Locking should be done by overviewPage::accessLock.
+//  CALLEE is responsible for obtaining and releasing the lock
+//
+RecData
+overviewRecord::readData(RecDataT varType, const char *name, bool * found)
+{
+  int rec_status = REC_ERR_OKAY;
+  int order = -1;
+  RecData rec;
+  RecDataClear(RECD_NULL, &rec);
+
+  if (localNode == false) {
+    rec_status = RecGetRecordOrderAndId(name, &order, NULL);
+    if (rec_status == REC_ERR_OKAY) {
+      order -= node_rec_first_ix; // Offset
+      ink_release_assert(order < node_rec_data.num_recs);
+      ink_debug_assert(order < node_rec_data.num_recs);
+      RecDataSet(varType, &rec, &node_rec_data.recs[order].data);
+    } else {
+      Fatal("node variables '%s' not found!\n", name);
+    }
+  } else
+    rec_status = RecGetRecord_Xmalloc(name, varType, &rec, true);
+
+  if (found) {
+    *found = (rec_status == REC_ERR_OKAY);
+  } else {
+    mgmt_log(stderr, "node variables '%s' not found!\n");
+  }
+  return rec;
+}
+
 // bool overviewRecord::varStrFromName (char*, char*bufVal, char*, int)
 //
 //  Accessor function for node records.  Looks up varName for
@@ -1415,6 +1454,39 @@ overviewPage::clusterSumFloat(const char *nodeVar, RecFloat * sum)
   return numUsed;
 }
 
+// int overviewPage::clusterSumData(RecDataT varType, const char* nodeVar,
+//                                  RecData* sum)
+//
+//   Sums nodeVar for every up node in the cluster and stores the
+//     sum in *sum.  Returns the number of nodes summed over
+//
+//   CALLEE MUST HOLD this->accessLock
+//
+int
+overviewPage::clusterSumData(RecDataT varType, const char *nodeVar,
+                             RecData *sum)
+{
+  int numUsed = 0;
+  int numHosts_local = sortRecords.getNumEntries();
+  overviewRecord *current;
+  bool found;
+  RecData recTmp;
+
+  ink_assert(sum != NULL);
+  RecDataClear(varType, sum);
+
+  for (int i = 0; i < numHosts_local; i++) {
+    current = (overviewRecord *) sortRecords[i];
+    if (current->up == true) {
+      numUsed++;
+      recTmp = current->readData(varType, nodeVar, &found);
+      *sum = RecDataAdd(varType, *sum, recTmp);
+      if (found == false) {
+      }
+    }
+  }
+  return numUsed;
+}
 
 // void overviewPage::clusterAgFloat(const char* clusterVar, const char* nodeVar)
 //
@@ -1460,6 +1532,21 @@ overviewPage::varClusterFloatFromName(char *nodeVar, RecFloat * sum)
   return (status);
 }
 
+int
+overviewPage::varClusterDataFromName(RecDataT varType, char *nodeVar,
+                                     RecData *sum)
+{
+  int status = 0;
+
+  ink_mutex_acquire(&accessLock);
+
+  status = clusterSumData(varType, nodeVar, sum);
+
+  ink_mutex_release(&accessLock);
+
+  return (status);
+}
+
 // Moved from the now removed StatAggregation.cc
 void
 AgFloat_generic_scale_to_int(const char *processVar, const char *nodeVar, double factor)

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/5bcc19e6/mgmt/web2/WebOverview.h
----------------------------------------------------------------------
diff --git a/mgmt/web2/WebOverview.h b/mgmt/web2/WebOverview.h
index dbb037a..1bbb506 100644
--- a/mgmt/web2/WebOverview.h
+++ b/mgmt/web2/WebOverview.h
@@ -106,6 +106,7 @@ public:
   RecInt readInteger(const char *name, bool * found);
   RecFloat readFloat(const char *name, bool * found);
   RecString readString(const char *name, bool * found);
+  RecData readData(RecDataT varType, const char *name, bool * found);
   bool varIntFromName(const char *varName, RecInt * value);
   bool varFloatFromName(const char *varName, RecFloat * value);
   bool varCounterFromName(const char *varName, RecCounter * value);
@@ -135,6 +136,7 @@ public:
   void addSelfRecord();
 
   int varClusterFloatFromName(char *, MgmtFloat *);
+  int varClusterDataFromName(RecDataT varType, char *nodeVar, RecData *sum);
 
 private:
   ink_mutex accessLock;
@@ -165,6 +167,7 @@ private:
   int clusterSumCounter(char *nodeVar, MgmtInt * sum);
   void clusterAgFloat(const char *clusterVar, const char *nodeVar);
   int clusterSumFloat(const char *nodeVar, MgmtFloat * sum);
+  int clusterSumData(RecDataT varType, const char *nodeVar, RecData *sum);
   void agHostdbHitRate();
   void agCacheHitRate();
   void agCachePercentFree();