// Copyright 2016 Twitter. 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.twitter.heron.metricsmgr.sink.metricscache; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; import org.junit.After; import org.junit.Assert; import org.junit.Test; import com.twitter.heron.api.metric.MultiCountMetric; import com.twitter.heron.common.basics.SingletonRegistry; import com.twitter.heron.metricsmgr.sink.SinkContextImpl; import com.twitter.heron.proto.tmaster.TopologyMaster; import com.twitter.heron.spi.metricsmgr.sink.SinkContext; /** * MetricsCacheSink Tester. */ public class MetricsCacheSinkTest { // Bean name to register the MetricsCacheLocation object into SingletonRegistry private static final String METRICSCACHE_LOCATION_BEAN_NAME = TopologyMaster.MetricsCacheLocation.newBuilder().getDescriptorForType().getFullName(); private static final int RECONNECT_INTERVAL_SECONDS = 1; private static final int RESTART_WAIT_INTERVAL_SECONDS = 5; private static final int METRICSCACHE_LOCATION_CHECK_INTERVAL_SECONDS = 1; private static final int WAIT_SECONDS = 10; private static Map<String, Object> buildServiceConfig() { Map<String, Object> serviceConfig = new HashMap<String, Object>(); // Fill with necessary config serviceConfig.put("reconnect-interval-second", RECONNECT_INTERVAL_SECONDS); serviceConfig.put("network-write-batch-size-bytes", 1); serviceConfig.put("network-write-batch-time-ms", 1); serviceConfig.put("network-read-batch-size-bytes", 1); serviceConfig.put("network-read-batch-time-ms", 1); serviceConfig.put("socket-send-buffer-size-bytes", 1); serviceConfig.put("socket-received-buffer-size-bytes", 1); return serviceConfig; } @After @SuppressWarnings("unchecked") public void after() throws Exception { // Remove the Singleton by Reflection Field field = SingletonRegistry.INSTANCE.getClass().getDeclaredField("singletonObjects"); field.setAccessible(true); Map<String, Object> singletonObjects = (Map<String, Object>) field.get(SingletonRegistry.INSTANCE); singletonObjects.clear(); } /** * Test automatic recover from uncaught exceptions in MetricsCacheClient */ @Test public void testMetricsCacheClientService() throws Exception { // create a new MetricsCacheClientService MetricsCacheSink metricsCacheSink = new MetricsCacheSink(); Map<String, Object> serviceConfig = buildServiceConfig(); metricsCacheSink.createSimpleMetricsCacheClientService(serviceConfig); // Notice here we set host and port as invalid values // So MetricsCache would throw "java.nio.channels.UnresolvedAddressException" once it starts, // and then dies TopologyMaster.MetricsCacheLocation location = TopologyMaster.MetricsCacheLocation.newBuilder(). setTopologyName("topology-name").setTopologyId("topology-id").setHost("host"). setControllerPort(0).setMasterPort(0).setStatsPort(0).build(); metricsCacheSink.startNewMetricsCacheClient(location); // We wait for a while to let auto recover fully finish. Thread.sleep(RESTART_WAIT_INTERVAL_SECONDS * 1000); // Then we check whether the MetricsCacheService has restarted the MetricsCacheClient for // several times Take other factors into account, we would check whether the MetricsCacheClient // has restarted at least half the RESTART_WAIT_INTERVAL_SECONDS/RECONNECT_INTERVAL_SECONDS Assert.assertTrue(metricsCacheSink.getMetricsCacheStartedAttempts() > (RESTART_WAIT_INTERVAL_SECONDS / RECONNECT_INTERVAL_SECONDS / 2)); metricsCacheSink.close(); } /** * Test whether MetricsCacheSink would handle MetricsCacheLocation in SingletonRegistry automatically */ @Test public void testHandleMetricsCacheLocation() throws Exception { // create a new MetricsCacheClientService MetricsCacheSink metricsCacheSink = new MetricsCacheSink(); Map<String, Object> sinkConfig = new HashMap<String, Object>(); // Fill with necessary config sinkConfig.put( "metricscache-location-check-interval-sec", METRICSCACHE_LOCATION_CHECK_INTERVAL_SECONDS); // These are config for MetricsCacheClient Map<String, Object> serviceConfig = buildServiceConfig(); sinkConfig.put("metricscache-client", serviceConfig); // It is null since we have not set it Assert.assertNull(metricsCacheSink.getCurrentMetricsCacheLocation()); SinkContext sinkContext = new SinkContextImpl("topology-name", "metricsmgr-id", "sink-id", new MultiCountMetric()); // Start the MetricsCacheSink metricsCacheSink.init(sinkConfig, sinkContext); // Put the MetricsCacheLocation into SingletonRegistry TopologyMaster.MetricsCacheLocation oldLoc = TopologyMaster.MetricsCacheLocation.newBuilder(). setTopologyName("topology-name").setTopologyId("topology-id"). setHost("host").setControllerPort(0).setMasterPort(0).build(); SingletonRegistry.INSTANCE.registerSingleton(METRICSCACHE_LOCATION_BEAN_NAME, oldLoc); Thread.sleep(WAIT_SECONDS * 1000); // The MetricsCacheService should start Assert.assertTrue(metricsCacheSink.getMetricsCacheStartedAttempts() > 0); Assert.assertEquals(oldLoc, metricsCacheSink.getCurrentMetricsCacheLocation()); Assert.assertEquals(oldLoc, metricsCacheSink.getCurrentMetricsCacheLocationInService()); // Update it, the MetricsCacheSink should pick up the new one. TopologyMaster.MetricsCacheLocation newLoc = TopologyMaster.MetricsCacheLocation.newBuilder(). setTopologyName("topology-name").setTopologyId("topology-id"). setHost("host").setControllerPort(0).setMasterPort(1).build(); SingletonRegistry.INSTANCE.updateSingleton(METRICSCACHE_LOCATION_BEAN_NAME, newLoc); int lastMetricsCacheStartedAttempts = metricsCacheSink.getMetricsCacheStartedAttempts(); Thread.sleep(WAIT_SECONDS * 1000); // The MetricsCacheService should use the new MetricsCacheLocation Assert.assertTrue( metricsCacheSink.getMetricsCacheStartedAttempts() > lastMetricsCacheStartedAttempts); Assert.assertNotSame(oldLoc, metricsCacheSink.getCurrentMetricsCacheLocation()); Assert.assertNotSame(oldLoc, metricsCacheSink.getCurrentMetricsCacheLocationInService()); Assert.assertEquals(newLoc, metricsCacheSink.getCurrentMetricsCacheLocation()); Assert.assertEquals(newLoc, metricsCacheSink.getCurrentMetricsCacheLocationInService()); metricsCacheSink.close(); } }