Package com.linkedin.databus.client.pub

Source Code of com.linkedin.databus.client.pub.TestUnifiedClientStats

/*
* Copyright 2014 LinkedIn Corp. All rights reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.linkedin.databus.client.pub;

import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertTrue;
import static org.testng.AssertJUnit.fail;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;

import org.apache.avro.Schema;
//import org.apache.commons.math3.stat.StatUtils;
import org.testng.annotations.Test;

import com.codahale.metrics.MergeableExponentiallyDecayingReservoir;

import com.linkedin.databus.client.pub.mbean.UnifiedClientStats;
import com.linkedin.databus.core.DbusConstants;
import com.linkedin.databus.core.DbusEvent;
import com.linkedin.databus.core.DbusEventFactory;
import com.linkedin.databus.core.DbusEventInfo;
import com.linkedin.databus.core.DbusEventKey;
import com.linkedin.databus.core.DbusEventV2Factory;
import com.linkedin.databus.core.DbusOpcode;
import com.linkedin.databus.core.KeyTypeNotImplementedException;
import com.linkedin.databus2.schemas.utils.SchemaHelper;

/**
* Mainly tests the histogram/percentile metrics of UnifiedClientStats, including multi-connection
* aggregation (merging).
*
* See also TestGenericDispatcher (unit test; tests numConsumerErrors) and TestRelayBootstrapSwitch
* (integration test; tests all remaining UnifiedClientStats metrics:  curBootstrappingPartitions,
* curDeadConnections, numDataEvents, timeLagLastReceivedToNowMs).
*/
public class TestUnifiedClientStats
{
  private static final Schema SOURCE1_SCHEMA =
      Schema.parse("{\"name\":\"source1\",\"type\":\"record\",\"fields\":[{\"name\":\"s\",\"type\":\"string\"}]}");
  private static final String SOURCE1_SCHEMA_STR = SOURCE1_SCHEMA.toString();
  private static final byte[] SOURCE1_SCHEMAID = SchemaHelper.getSchemaId(SOURCE1_SCHEMA_STR);
  private static final byte[] SOURCE1_PAYLOAD = new byte[] { 0x67, 0x72, 0x6f, 0x6e, 0x6b };
  // alternatively:    byte[] SOURCE1_PAYLOAD = javax.xml.bind.DatatypeConverter.parseHexBinary("67726f6e6b");

  private DbusEventFactory _eventFactory = new DbusEventV2Factory();


  private DbusEvent createEvent(long timestampNs)
  {
    DbusEventInfo eventInfo = new DbusEventInfo(DbusOpcode.UPSERT,
                                                6004L,             // SCN
                                                (short) 1,         // physical partition ID
                                                (short) 1,         // logical partition ID
                                                timestampNs,
                                                (short) 1,         // srcId
                                                SOURCE1_SCHEMAID,  // payloadSchemaMd5
                                                SOURCE1_PAYLOAD,   // payload
                                                false,             // enableTracing
                                                true);             // autocommit
    DbusEventKey key = new DbusEventKey("myKey".getBytes(Charset.forName("UTF-8")));
    ByteBuffer buf = ByteBuffer.allocate(1000).order(ByteOrder.BIG_ENDIAN);
    try
    {
      DbusEventFactory.serializeEvent(key, buf, eventInfo);
    }
    catch (KeyTypeNotImplementedException ex)
    {
      fail("string key type not supported by DbusEventV2Factory?!? " + ex.getLocalizedMessage());
    }
    return _eventFactory.createReadOnlyDbusEventFromBuffer(buf, 0);
  }


  /**
   * Tests the basic (non-aggregated) functionality of the histogram/percentile metrics
   * (timeLagSourceToReceiptMs and timeLagConsumerCallbacksMs).
   */
  @Test
  public void testBasicHistogramMetrics()
  {
    // (1) create stats object
    UnifiedClientStats unifiedClientStats = new UnifiedClientStats(3 /* ownerId */, "stats_name", "stats_dim");

    for (int i = 0; i < 200; ++i)
    {
      // Without the ability to override System.currentTimeMillis() (or hacking UnifiedClientStats to use an
      // overridable method to provide the time, and then overriding it here), there's a small chance that
      // our System.currentTimeMillis() call and that in registerDataEventReceived() will return values that
      // differ by a non-constant amount (i.e., jitter).  But we can manage that with inequalities in our
      // assertions.
      // Expected histogram values for timeLagSourceToReceiptMs range from 0 to 1990 ms (approximately).
      long sourceTimestampNs = (System.currentTimeMillis() - 10*i) * DbusConstants.NUM_NSECS_IN_MSEC;

      // We have perfect control over the values for timeLagConsumerCallbacksMs.  Make calculations trivial:
      // histogram values will be 0 through 199 ms (exactly).
      long callbackTimeElapsedNs = (long)i * DbusConstants.NUM_NSECS_IN_MSEC;

      // (2) create 200 fake DbusEvents
      DbusEvent dbusEvent = createEvent(sourceTimestampNs);

      // (3) call registerDataEventReceived() and registerCallbacksProcessed() for each event
      // (normally there are more of the latter since there are more callback types than just onDataEvent(),
      // but it doesn't really matter, and it simplifies things if we keep a fixed ratio--here just 1:1)
      unifiedClientStats.registerDataEventReceived(dbusEvent);
      unifiedClientStats.registerCallbacksProcessed(callbackTimeElapsedNs);
    }

    // (4) verify histogram values are as expected

    // Both metrics-core and Apache Commons Math use the "R-6" quantile-estimation method, as described
    // at http://en.wikipedia.org/wiki/Quantile .
    //
    // N = 200
    // p = 0.5, 0.9, 0.95, 0.99
    // h = (N+1)*p = 100.5, 180.9, 190.95, 198.99
    //
    // Q[50th]  =  x[100-1] + (100.5  - 100)*(x[100-1+1] - x[100-1])  =   99.0 + 0.5 *(100.0 -  99.0)  =   99.5
    // Q[90th]  =  x[180-1] + (180.9  - 180)*(x[180-1+1] - x[180-1])  =  179.0 + 0.9 *(180.0 - 179.0)  =  179.9
    // Q[95th]  =  x[190-1] + (190.95 - 190)*(x[190-1+1] - x[190-1])  =  189.0 + 0.95*(190.0 - 189.0)  =  189.95
    // Q[99th]  =  x[198-1] + (198.99 - 198)*(x[198-1+1] - x[198-1])  =  197.0 + 0.99*(198.0 - 197.0)  =  197.99
    assertEquals("unexpected timeLagConsumerCallbacksMs 50th percentile",
                 99.5,
                 unifiedClientStats.getTimeLagConsumerCallbacksMs_HistPct_50());
    assertEquals("unexpected timeLagConsumerCallbacksMs 90th percentile",
                 179.9,
                 unifiedClientStats.getTimeLagConsumerCallbacksMs_HistPct_90());
    assertEquals("unexpected timeLagConsumerCallbacksMs 95th percentile",
                 189.95,
                 unifiedClientStats.getTimeLagConsumerCallbacksMs_HistPct_95());
    assertEquals("unexpected timeLagConsumerCallbacksMs 99th percentile",
                 197.99,
                 unifiedClientStats.getTimeLagConsumerCallbacksMs_HistPct_99());
    assertEquals("unexpected timeLagConsumerCallbacksMs max value",
                 199.0,
                 unifiedClientStats.getTimeLagConsumerCallbacksMs_Max());

    // See sourceTimestampNs comment above.  Approximately:
    // Q[50th]  =  x[100-1] + (100.5  - 100)*(x[100-1+1] - x[100-1])  =   990.0 + 0.5 *(1000.0 -  990.0)  =   995.0
    // Q[90th]  =  x[180-1] + (180.9  - 180)*(x[180-1+1] - x[180-1])  =  1790.0 + 0.9 *(1800.0 - 1790.0)  =  1799.0
    // Q[95th]  =  x[190-1] + (190.95 - 190)*(x[190-1+1] - x[190-1])  =  1890.0 + 0.95*(1900.0 - 1890.0)  =  1899.5
    // Q[99th]  =  x[198-1] + (198.99 - 198)*(x[198-1+1] - x[198-1])  =  1970.0 + 0.99*(1980.0 - 1970.0)  =  1979.9
    // ...but allow +/-1 for jitter
    double percentile = unifiedClientStats.getTimeLagSourceToReceiptMs_HistPct_50();
    assertTrue("unexpected timeLagSourceToReceiptMs 50th percentile: " + percentile,
               994.0 <= percentile && percentile <= 996.0);    // nominal value is 995.0

    percentile = unifiedClientStats.getTimeLagSourceToReceiptMs_HistPct_90();
    assertTrue("unexpected timeLagSourceToReceiptMs 90th percentile: " + percentile,
               1798.0 <= percentile && percentile <= 1800.0)// nominal value is 1799.0

    percentile = unifiedClientStats.getTimeLagSourceToReceiptMs_HistPct_95();
    assertTrue("unexpected timeLagSourceToReceiptMs 95th percentile: " + percentile,
               1898.5 <= percentile && percentile <= 1900.5)// nominal value is 1899.5, but saw 1900.45 once

    percentile = unifiedClientStats.getTimeLagSourceToReceiptMs_HistPct_99();
    assertTrue("unexpected timeLagSourceToReceiptMs 99th percentile: " + percentile,
               1978.9 <= percentile && percentile <= 1980.9)// nominal value is 1979.9
  }


  /**
   * Tests aggregation (merging) of the timeLagSourceToReceiptMs histogram/percentile metric in the case
   * that one of the connections is bootstrapping.
   *
   * Blast out 1000 data values for stats #1 and #2, but with the latter in bootstrap mode:
   * timestampLastDataEventWasReceivedMs will be zero for stats #2 (and its reservoir empty), so
   * merging it won't affect the aggregate value for timeLagSourceToReceiptMs; all such aggregate
   * stats should be identical to those for stats #1.  Also, all values for stats #2 should be -1.0,
   * per our design spec.  (This is similar to testHistogramMetricsAggregationDeadSourcesConnection().)
   */
  @Test
  public void testHistogramMetricsAggregationBootstrapMode()
  {
    // create stats objects:  two low-level (per-connection) ones and one aggregator
    UnifiedClientStats unifiedClientStats1 = new UnifiedClientStats(1 /* ownerId */, "test1", "dim1");
    UnifiedClientStats unifiedClientStats2 = new UnifiedClientStats(2 /* ownerId */, "test2", "dim2");
    UnifiedClientStats unifiedClientStatsAgg = new UnifiedClientStats(99 /* ownerId */, "testAgg", "dimAgg");

    unifiedClientStats2.setBootstrappingState(true);

    for (int i = 0; i < MergeableExponentiallyDecayingReservoir.DEFAULT_SIZE; ++i// 1028
    {
      long now = System.currentTimeMillis();
      long sourceTimestampNs1 = (now - 1000L - i) * DbusConstants.NUM_NSECS_IN_MSEC;
      long sourceTimestampNs2 = (now - 5000L - i) * DbusConstants.NUM_NSECS_IN_MSEC;

      unifiedClientStats1.registerDataEventReceived(createEvent(sourceTimestampNs1));
      unifiedClientStats2.registerDataEventReceived(createEvent(sourceTimestampNs2));
    }

    unifiedClientStatsAgg.merge(unifiedClientStats1);
    unifiedClientStatsAgg.merge(unifiedClientStats2);

    assertEquals("unexpected timeLagSourceToReceiptMs 50th percentile for aggregated stats",
                 unifiedClientStats1.getTimeLagSourceToReceiptMs_HistPct_50(),
                 unifiedClientStatsAgg.getTimeLagSourceToReceiptMs_HistPct_50());
    assertEquals("unexpected timeLagSourceToReceiptMs 90th percentile for aggregated stats",
                 unifiedClientStats1.getTimeLagSourceToReceiptMs_HistPct_90(),
                 unifiedClientStatsAgg.getTimeLagSourceToReceiptMs_HistPct_90());
    assertEquals("unexpected timeLagSourceToReceiptMs 95th percentile for aggregated stats",
                 unifiedClientStats1.getTimeLagSourceToReceiptMs_HistPct_95(),
                 unifiedClientStatsAgg.getTimeLagSourceToReceiptMs_HistPct_95());
    assertEquals("unexpected timeLagSourceToReceiptMs 99th percentile for aggregated stats",
                 unifiedClientStats1.getTimeLagSourceToReceiptMs_HistPct_99(),
                 unifiedClientStatsAgg.getTimeLagSourceToReceiptMs_HistPct_99());

    // bootstrap mode => should return -1.0 for all percentiles
    assertEquals("unexpected timeLagSourceToReceiptMs 50th percentile for connection #2",
                 -1.0,
                 unifiedClientStats2.getTimeLagSourceToReceiptMs_HistPct_50());
    assertEquals("unexpected timeLagSourceToReceiptMs 90th percentile for connection #2",
                 -1.0,
                 unifiedClientStats2.getTimeLagSourceToReceiptMs_HistPct_90());
    assertEquals("unexpected timeLagSourceToReceiptMs 95th percentile for connection #2",
                 -1.0,
                 unifiedClientStats2.getTimeLagSourceToReceiptMs_HistPct_95());
    assertEquals("unexpected timeLagSourceToReceiptMs 99th percentile for connection #2",
                 -1.0,
                 unifiedClientStats2.getTimeLagSourceToReceiptMs_HistPct_99());
  }


  /**
   * Tests aggregation (merging) of the timeLagSourceToReceiptMs histogram/percentile metric in the case
   * that one of the connections is dead (i.e., no data events received).
   *
   * Blast out 1000 data values for stats #1 but none for stats #2 (in particular, no registerDataEventReceived()
   * calls):  timestampLastDataEventWasReceivedMs will be zero for stats #2 (and its reservoir empty), so
   * merging it won't affect the aggregate value for timeLagSourceToReceiptMs; all such aggregate stats should
   * be identical to those for stats #1.  Also, all values for stats #2 should be -1.0, per our design spec.
   * (This is similar to testHistogramMetricsAggregationBootstrapMode().)
   */
  @Test
  public void testHistogramMetricsAggregationDeadSourcesConnection()
  {
    // create stats objects:  two low-level (per-connection) ones and one aggregator
    UnifiedClientStats unifiedClientStats1 = new UnifiedClientStats(1 /* ownerId */, "test1", "dim1");
    UnifiedClientStats unifiedClientStats2 = new UnifiedClientStats(2 /* ownerId */, "test2", "dim2");
    UnifiedClientStats unifiedClientStatsAgg = new UnifiedClientStats(99 /* ownerId */, "testAgg", "dimAgg");

    // could break this into two (or more) parts and do multiple merges, but not clear there's any point...
    for (int i = 0; i < 2*MergeableExponentiallyDecayingReservoir.DEFAULT_SIZE; ++i// 2*1028
    {
      long sourceTimestampNs1 = (System.currentTimeMillis() - 1000L - i) * DbusConstants.NUM_NSECS_IN_MSEC;
      // no data events for connection #2 => no need for sourceTimestampNs2

      unifiedClientStats1.registerDataEventReceived(createEvent(sourceTimestampNs1));
    }

    unifiedClientStatsAgg.merge(unifiedClientStats1);
    unifiedClientStatsAgg.merge(unifiedClientStats2);

    assertEquals("unexpected timeLagSourceToReceiptMs 50th percentile for aggregated stats",
                 unifiedClientStats1.getTimeLagSourceToReceiptMs_HistPct_50(),
                 unifiedClientStatsAgg.getTimeLagSourceToReceiptMs_HistPct_50());
    assertEquals("unexpected timeLagSourceToReceiptMs 90th percentile for aggregated stats",
                 unifiedClientStats1.getTimeLagSourceToReceiptMs_HistPct_90(),
                 unifiedClientStatsAgg.getTimeLagSourceToReceiptMs_HistPct_90());
    assertEquals("unexpected timeLagSourceToReceiptMs 95th percentile for aggregated stats",
                 unifiedClientStats1.getTimeLagSourceToReceiptMs_HistPct_95(),
                 unifiedClientStatsAgg.getTimeLagSourceToReceiptMs_HistPct_95());
    assertEquals("unexpected timeLagSourceToReceiptMs 99th percentile for aggregated stats",
                 unifiedClientStats1.getTimeLagSourceToReceiptMs_HistPct_99(),
                 unifiedClientStatsAgg.getTimeLagSourceToReceiptMs_HistPct_99());

    // no data values => should return -1.0 for all percentiles
    assertEquals("unexpected timeLagSourceToReceiptMs 50th percentile for connection #2",
                 -1.0,
                 unifiedClientStats2.getTimeLagSourceToReceiptMs_HistPct_50());
    assertEquals("unexpected timeLagSourceToReceiptMs 90th percentile for connection #2",
                 -1.0,
                 unifiedClientStats2.getTimeLagSourceToReceiptMs_HistPct_90());
    assertEquals("unexpected timeLagSourceToReceiptMs 95th percentile for connection #2",
                 -1.0,
                 unifiedClientStats2.getTimeLagSourceToReceiptMs_HistPct_95());
    assertEquals("unexpected timeLagSourceToReceiptMs 99th percentile for connection #2",
                 -1.0,
                 unifiedClientStats2.getTimeLagSourceToReceiptMs_HistPct_99());
  }


  /**
   * Tests aggregation (merging) of the histogram/percentile metrics (timeLagSourceToReceiptMs and
   * timeLagConsumerCallbacksMs) in the case that there have been no data events or callbacks, i.e.,
   * there are no data points in the histogram reservoirs.  All timeLagSourceToReceiptMs metrics
   * and timeLagConsumerCallbacksMs metrics should be -1.0, per the design spec.
   */
  @Test
  public void testHistogramMetricsAggregationNoData()
  {
    UnifiedClientStats unifiedClientStats1 = new UnifiedClientStats(1 /* ownerId */, "test1", "dim1");
    UnifiedClientStats unifiedClientStats2 = new UnifiedClientStats(2 /* ownerId */, "test2", "dim2");
    UnifiedClientStats unifiedClientStatsAgg = new UnifiedClientStats(99 /* ownerId */, "testAgg", "dimAgg");

    assertEquals("unexpected timeLagSourceToReceiptMs 50th percentile for connection #1",
                 -1.0,
                 unifiedClientStats1.getTimeLagSourceToReceiptMs_HistPct_50());
    assertEquals("unexpected timeLagSourceToReceiptMs 90th percentile for connection #1",
                 -1.0,
                 unifiedClientStats1.getTimeLagSourceToReceiptMs_HistPct_90());
    assertEquals("unexpected timeLagSourceToReceiptMs 95th percentile for connection #1",
                 -1.0,
                 unifiedClientStats1.getTimeLagSourceToReceiptMs_HistPct_95());
    assertEquals("unexpected timeLagSourceToReceiptMs 99th percentile for connection #1",
                 -1.0,
                 unifiedClientStats1.getTimeLagSourceToReceiptMs_HistPct_99());

    assertEquals("unexpected timeLagSourceToReceiptMs 50th percentile for connection #2",
                 -1.0,
                 unifiedClientStats2.getTimeLagSourceToReceiptMs_HistPct_50());
    assertEquals("unexpected timeLagSourceToReceiptMs 90th percentile for connection #2",
                 -1.0,
                 unifiedClientStats2.getTimeLagSourceToReceiptMs_HistPct_90());
    assertEquals("unexpected timeLagSourceToReceiptMs 95th percentile for connection #2",
                 -1.0,
                 unifiedClientStats2.getTimeLagSourceToReceiptMs_HistPct_95());
    assertEquals("unexpected timeLagSourceToReceiptMs 99th percentile for connection #2",
                 -1.0,
                 unifiedClientStats2.getTimeLagSourceToReceiptMs_HistPct_99());

    assertEquals("unexpected timeLagSourceToReceiptMs 50th percentile for aggregated stats",
                 -1.0,
                 unifiedClientStatsAgg.getTimeLagSourceToReceiptMs_HistPct_50());
    assertEquals("unexpected timeLagSourceToReceiptMs 90th percentile for aggregated stats",
                 -1.0,
                 unifiedClientStatsAgg.getTimeLagSourceToReceiptMs_HistPct_90());
    assertEquals("unexpected timeLagSourceToReceiptMs 95th percentile for aggregated stats",
                 -1.0,
                 unifiedClientStatsAgg.getTimeLagSourceToReceiptMs_HistPct_95());
    assertEquals("unexpected timeLagSourceToReceiptMs 99th percentile for aggregated stats",
                 -1.0,
                 unifiedClientStatsAgg.getTimeLagSourceToReceiptMs_HistPct_99());


    assertEquals("unexpected timeLagConsumerCallbacksMs 50th percentile for connection #1",
                 -1.0,
                 unifiedClientStats1.getTimeLagConsumerCallbacksMs_HistPct_50());
    assertEquals("unexpected timeLagConsumerCallbacksMs 90th percentile for connection #1",
                 -1.0,
                 unifiedClientStats1.getTimeLagConsumerCallbacksMs_HistPct_90());
    assertEquals("unexpected timeLagConsumerCallbacksMs 95th percentile for connection #1",
                 -1.0,
                 unifiedClientStats1.getTimeLagConsumerCallbacksMs_HistPct_95());
    assertEquals("unexpected timeLagConsumerCallbacksMs 99th percentile for connection #1",
                 -1.0,
                 unifiedClientStats1.getTimeLagConsumerCallbacksMs_HistPct_99());
    assertEquals("unexpected timeLagConsumerCallbacksMs max for connection #1",
                 -1.0,
                 unifiedClientStats1.getTimeLagConsumerCallbacksMs_Max());

    assertEquals("unexpected timeLagConsumerCallbacksMs 50th percentile for connection #2",
                 -1.0,
                 unifiedClientStats2.getTimeLagConsumerCallbacksMs_HistPct_50());
    assertEquals("unexpected timeLagConsumerCallbacksMs 90th percentile for connection #2",
                 -1.0,
                 unifiedClientStats2.getTimeLagConsumerCallbacksMs_HistPct_90());
    assertEquals("unexpected timeLagConsumerCallbacksMs 95th percentile for connection #2",
                 -1.0,
                 unifiedClientStats2.getTimeLagConsumerCallbacksMs_HistPct_95());
    assertEquals("unexpected timeLagConsumerCallbacksMs 99th percentile for connection #2",
                 -1.0,
                 unifiedClientStats2.getTimeLagConsumerCallbacksMs_HistPct_99());
    assertEquals("unexpected timeLagConsumerCallbacksMs max for connection #2",
                 -1.0,
                 unifiedClientStats2.getTimeLagConsumerCallbacksMs_Max());

    assertEquals("unexpected timeLagConsumerCallbacksMs 50th percentile for aggregated stats",
                 -1.0,
                 unifiedClientStatsAgg.getTimeLagConsumerCallbacksMs_HistPct_50());
    assertEquals("unexpected timeLagConsumerCallbacksMs 90th percentile for aggregated stats",
                 -1.0,
                 unifiedClientStatsAgg.getTimeLagConsumerCallbacksMs_HistPct_90());
    assertEquals("unexpected timeLagConsumerCallbacksMs 95th percentile for aggregated stats",
                 -1.0,
                 unifiedClientStatsAgg.getTimeLagConsumerCallbacksMs_HistPct_95());
    assertEquals("unexpected timeLagConsumerCallbacksMs 99th percentile for aggregated stats",
                 -1.0,
                 unifiedClientStatsAgg.getTimeLagConsumerCallbacksMs_HistPct_99());
    assertEquals("unexpected timeLagConsumerCallbacksMs max for aggregated stats",
                 -1.0,
                 unifiedClientStatsAgg.getTimeLagConsumerCallbacksMs_Max());
  }


  /**
   * Tests aggregation (merging) of the histogram/percentile metrics (timeLagSourceToReceiptMs and
   * timeLagConsumerCallbacksMs).  This is basically the "happy path" case.
   *
   * Blast out 1000 low data values for stats #1 and 1000 high data values for stats #2 (interleaved so
   * timestamps [and therefore priorities] are comparable), then merge and verify that max is within #2's
   * range and that median falls between the two ranges.  (There's no guarantee that #1's minimum or #2's
   * maximum will survive, but roughly half of each one's values should, so the min and max are guaranteed
   * to fall within #1's and #2's range, respectively.)
   */
  @Test
  public void testHistogramMetricsAggregationNonOverlappingRanges()
  {
    // create stats objects:  two low-level (per-connection) ones and one aggregator
    UnifiedClientStats unifiedClientStats1 = new UnifiedClientStats(1 /* ownerId */, "test1", "dim1");
    UnifiedClientStats unifiedClientStats2 = new UnifiedClientStats(2 /* ownerId */, "test2", "dim2");
    UnifiedClientStats unifiedClientStatsAgg = new UnifiedClientStats(99 /* ownerId */, "testAgg", "dimAgg");

    for (int i = 0; i < MergeableExponentiallyDecayingReservoir.DEFAULT_SIZE; ++i// 1028
    {
      // As noted in testBasicHistogramMetrics(), our dependence on System.currentTimeMillis() may lead
      // to some jitter in the data values for timeLagSourceToReceiptMs.
      long now = System.currentTimeMillis();
      long sourceTimestampNs1 = (now - 1000L - i) * DbusConstants.NUM_NSECS_IN_MSEC;
      long sourceTimestampNs2 = (now - 5000L - i) * DbusConstants.NUM_NSECS_IN_MSEC;

      long callbackTimeElapsedNs1 = (long)i * DbusConstants.NUM_NSECS_IN_MSEC;
      long callbackTimeElapsedNs2 = ((long)i + 2000L) * DbusConstants.NUM_NSECS_IN_MSEC;

      DbusEvent dbusEvent1 = createEvent(sourceTimestampNs1);
      DbusEvent dbusEvent2 = createEvent(sourceTimestampNs2);

      unifiedClientStats1.registerDataEventReceived(dbusEvent1);
      unifiedClientStats2.registerDataEventReceived(dbusEvent2);
      unifiedClientStats1.registerCallbacksProcessed(callbackTimeElapsedNs1);
      unifiedClientStats2.registerCallbacksProcessed(callbackTimeElapsedNs2);
    }

    unifiedClientStatsAgg.merge(unifiedClientStats1);
    unifiedClientStatsAgg.merge(unifiedClientStats2);

    // Expected timeLagConsumerCallbacksMs histogram values (exact):
    //   unifiedClientStats1:  0 to 1027 ms
    //   unifiedClientStats2:  2000 to 3027 ms

    assertEquals("unexpected timeLagConsumerCallbacksMs 50th percentile for connection #1",
                 513.5,
                 unifiedClientStats1.getTimeLagConsumerCallbacksMs_HistPct_50());
    assertEquals("unexpected timeLagConsumerCallbacksMs 50th percentile for connection #2",
                 2513.5,
                 unifiedClientStats2.getTimeLagConsumerCallbacksMs_HistPct_50());

    // The exact value depends on the relative fraction of '1' and '2' values that are retained in the
    // aggregate.  If equal, the value should be near 1513.5, but even then the exact value depends on
    // whether the 1027 and 2000 values get bumped out of the aggregate.  In the more common case that
    // the fractions retained are unequal, the median will fall between two values near the top end of
    // unifiedClientStats1 or near the bottom end of unifiedClientStats2.  An allowance of 100 either
    // way should be safe.
    double percentile = unifiedClientStatsAgg.getTimeLagConsumerCallbacksMs_HistPct_50();
    assertTrue("unexpected timeLagConsumerCallbacksMs 50th percentile for aggregated stats: " + percentile,
               927.0 <= percentile && percentile <= 2100.0);

    assertEquals("unexpected timeLagConsumerCallbacksMs max value for connection #1",
                 1027.0,
                 unifiedClientStats1.getTimeLagConsumerCallbacksMs_Max());
    assertEquals("unexpected timeLagConsumerCallbacksMs max value for connection #2",
                 3027.0,
                 unifiedClientStats2.getTimeLagConsumerCallbacksMs_Max());

    double max = unifiedClientStatsAgg.getTimeLagConsumerCallbacksMs_Max();
    assertTrue("unexpected timeLagConsumerCallbacksMs max value for aggregated stats: " + max,
               2000.0 <= max && max <= 3027.0)// nominal value is 3027.0

    // Expected timeLagSourceToReceiptMs histogram values (approximate):
    //   unifiedClientStats1:  1000 to 2027 ms
    //   unifiedClientStats2:  5000 to 6027 ms

    percentile = unifiedClientStats1.getTimeLagSourceToReceiptMs_HistPct_50();
    assertTrue("unexpected timeLagSourceToReceiptMs 50th percentile for connection #1: " + percentile,
               1512.5 <= percentile && percentile <= 1514.5);    // nominal value is 1513.5

    percentile = unifiedClientStats2.getTimeLagSourceToReceiptMs_HistPct_50();
    assertTrue("unexpected timeLagSourceToReceiptMs 50th percentile for connection #2: " + percentile,
               5512.5 <= percentile && percentile <= 5514.5);    // nominal value is 5513.5

    // same caveat as above:  the median depends strongly on the relative proportion of unifiedClientStats1
    // and unifiedClientStats2 data points retained in the aggregate, so the inequality is quite loose
    percentile = unifiedClientStatsAgg.getTimeLagSourceToReceiptMs_HistPct_50();
    assertTrue("unexpected timeLagSourceToReceiptMs 50th percentile for aggregated stats: " + percentile,
               1927.0 <= percentile && percentile <= 5100.0);
  }

}
TOP

Related Classes of com.linkedin.databus.client.pub.TestUnifiedClientStats

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.