co.cask.tigon.sql.internal.HealthAndMetricsTest.java Source code

Java tutorial

Introduction

Here is the source code for co.cask.tigon.sql.internal.HealthAndMetricsTest.java

Source

/*
 * Copyright  2014 Cask Data, Inc.
 *
 * 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 co.cask.tigon.sql.internal;

import co.cask.tigon.api.metrics.Metrics;
import co.cask.tigon.sql.conf.Constants;
import co.cask.tigon.sql.manager.DiscoveryServer;
import co.cask.tigon.sql.manager.HubDataStore;
import com.google.common.base.Throwables;
import com.google.common.collect.Maps;
import com.google.gson.JsonObject;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * HealthInspectorTest
 */

public class HealthAndMetricsTest {
    private static final Logger LOG = LoggerFactory.getLogger(HealthInspector.class);
    private static HealthInspector inspector;
    private static DiscoveryServer discoveryServer;
    private static CountDownLatch failureLatch;
    private static CountDownLatch pingLatch;
    private static Metrics metrics;
    private static final int PING_COUNT = 5;

    static class SharedMetrics implements Metrics {
        private static Map<String, Integer> metricsValue = Maps.newHashMap();

        @Override
        public void count(String counterName, int delta) {
            if (!metricsValue.containsKey(counterName)) {
                metricsValue.put(counterName, 0);
            }
            metricsValue.put(counterName, delta + metricsValue.get(counterName));
            LOG.info("[METRICS] CounterName : {}\tValue last second : {}", counterName, delta);
        }

        public static Integer getCounter(String counterName) {
            return metricsValue.get(counterName);
        }
    }

    @BeforeClass
    public static void setup() throws Exception {
        failureLatch = new CountDownLatch(1);
        pingLatch = new CountDownLatch(PING_COUNT);
        HubDataStore hubDataStore = new HubDataStore.Builder().setInstanceName("test").build();
        inspector = new HealthInspector(new ProcessMonitor() {
            @Override
            public void notifyFailure(Set<String> errorProcessNames) {
                LOG.info("Heartbeat detection failure notified");
                for (String error : errorProcessNames) {
                    LOG.error("No Heartbeat received from " + error);
                }
                failureLatch.countDown();
            }

            @Override
            public void announceReady() {
                //no-op
            }
        });

        metrics = new SharedMetrics();
        MetricsRecorder metricsRecorder = new MetricsRecorder(metrics);
        discoveryServer = new DiscoveryServer(hubDataStore, inspector, metricsRecorder, new ProcessMonitor() {
            @Override
            public void notifyFailure(Set<String> errorProcessNames) {
                //no-op
            }

            @Override
            public void announceReady() {
                //no-op
            }
        });
        discoveryServer.startAndWait();
    }

    private void register(String ftaID, String ftaName, String pingURL) {
        HttpClient httpClient = new DefaultHttpClient();
        JsonObject bodyJson = new JsonObject();
        bodyJson.addProperty("name", "test");
        bodyJson.addProperty("fta_name", ftaName);
        bodyJson.addProperty("ftaid", ftaID);

        HttpPost httpPost = new HttpPost(pingURL + "/v1/announce-fta-instance");
        StringEntity params = null;
        try {
            params = new StringEntity(bodyJson.toString());
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        httpPost.addHeader("Content-Type", "application/json");
        httpPost.setEntity(params);
        try {
            EntityUtils.consumeQuietly(httpClient.execute(httpPost).getEntity());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testPing() throws InterruptedException {
        final String pingURL = "http://" + discoveryServer.getHubAddress().getAddress().getHostAddress() + ":"
                + discoveryServer.getHubAddress().getPort();
        LOG.info("Discovery Server URL : " + pingURL);

        //Code to emulate pings after every second
        class MockPing implements Runnable {
            @Override
            public void run() {
                HttpClient httpClient = new DefaultHttpClient();
                for (int i = 0; i < PING_COUNT; i++) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    JsonObject bodyJson = new JsonObject();
                    bodyJson.addProperty("name", "test");
                    bodyJson.addProperty("ftaid", "IronMan");
                    JsonObject metrics = new JsonObject();
                    metrics.addProperty("awesomeCounter", i);
                    bodyJson.add("metrics", metrics);
                    HttpPost httpPost = new HttpPost(pingURL + "/v1/log-metrics");
                    StringEntity params = null;
                    try {
                        params = new StringEntity(bodyJson.toString());
                        httpPost.addHeader("Content-Type", "application/json");
                        httpPost.setEntity(params);
                        EntityUtils.consumeQuietly(httpClient.execute(httpPost).getEntity());
                    } catch (IOException e) {
                        Throwables.propagate(e);
                    }
                    LOG.info("Pinged with Metrics {}", bodyJson.toString());
                }
            }
        }

        inspector.startAndWait();
        LOG.info("Started monitoring...");

        TimeUnit.SECONDS.sleep(Constants.INITIALIZATION_TIMEOUT - 3);
        register("IronMan", "RobertDowneyJr", pingURL);
        LOG.info("IronMan Registered");
        Assert.assertTrue(!failureLatch.await(0, TimeUnit.SECONDS));
        LOG.info("No failure detected");

        //Initiate PING_COUNT mock pings, 1 per second
        new Thread(new MockPing()).start();

        LOG.info("Initiated {} mock pings", PING_COUNT);
        //Check state 250ms after the first mock ping
        Assert.assertTrue(!failureLatch.await(250, TimeUnit.MILLISECONDS));
        LOG.info("No failure Detected");

        LOG.info("Expecting heartbeat detection failure");
        pingLatch.await(30, TimeUnit.SECONDS);
        //Check state after the last mock ping (timeout > HEARTBEAT_FREQUENCY)
        Assert.assertTrue(failureLatch.await(30, TimeUnit.SECONDS));

        //Check value of awesomeCounter at the end
        Assert.assertTrue(SharedMetrics.getCounter("RobertDowneyJr.awesomeCounter")
                .equals((PING_COUNT - 1) * PING_COUNT / 2));
    }
}