com.comcast.cmb.test.tools.QueueDepthSimulator.java Source code

Java tutorial

Introduction

Here is the source code for com.comcast.cmb.test.tools.QueueDepthSimulator.java

Source

/**
 * Copyright 2012 Comcast Corporation
 * 
 * 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.comcast.cmb.test.tools;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.log4j.Logger;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

import com.comcast.cmb.common.util.Util;

public class QueueDepthSimulator {

    private static Logger logger = Logger.getLogger(QueueDepthSimulator.class);

    private static double deviationPct = 0.05;
    private static double transferFrequency = 0.1;
    private static int currentTime = 0;
    private static int duration = 3600;
    private static Random rand = new Random();

    private static long allAOut = 0;
    private static long allBOut = 0;

    public static Map<Integer, List<Event>> events = new HashMap<Integer, List<Event>>();
    public static List<SimpleQueue> queues = new ArrayList<SimpleQueue>();
    public static Map<String, List<Double>> logs = new HashMap<String, List<Double>>();
    public static TransferStrategy transferStrategy = new NopTransferStrategy();

    public static class Event {
        public int sendRateChange;
        public int receiveRateChange;
        public SimpleQueue q;

        public Event(SimpleQueue q, int sendRateChange, int receiveRateChange) {
            this.q = q;
            this.sendRateChange = sendRateChange;
            this.receiveRateChange = receiveRateChange;
        }
    }

    private static void clear() {
        currentTime = 0;
        allAOut = 0;
        allBOut = 0;
        events = new HashMap<Integer, List<Event>>();
        queues = new ArrayList<SimpleQueue>();
        logs = new HashMap<String, List<Double>>();
        transferStrategy = new NopTransferStrategy();
    }

    public static abstract class TransferStrategy {
        public abstract void transfer(SimpleQueue fromQ, SimpleQueue toQ);
    }

    public static class NopTransferStrategy extends TransferStrategy {
        @Override
        public void transfer(SimpleQueue fromQ, SimpleQueue toQ) {
        }
    }

    public static class DepthOnlyTransferStrategy extends TransferStrategy {
        private static int MAX = 1000;

        @Override
        public void transfer(SimpleQueue fromQ, SimpleQueue toQ) {
            if (fromQ.numMsg >= 10 * toQ.numMsg) {
                fromQ.lastTransferOut = Math.min(MAX, (int) (0.5 * (fromQ.numMsg - toQ.numMsg)));
                fromQ.numMsg -= fromQ.lastTransferOut;
                toQ.lastTransferIn = fromQ.lastTransferOut;
                toQ.numMsg += toQ.lastTransferIn;
            }
        }
    }

    public static class DepthAndRateStrategy extends TransferStrategy {
        private static int MAX = 1000;

        @Override
        public void transfer(SimpleQueue fromQ, SimpleQueue toQ) {
            if (fromQ.numMsg >= 10 * toQ.numMsg) {
                if (fromQ.lastSend / (fromQ.lastReceive + 1.0) > 1.1
                        && fromQ.lastSend / (fromQ.lastReceive + 1.0) > toQ.lastSend / (toQ.lastReceive + 1.0)) {
                    fromQ.lastTransferOut = Math.min(MAX, (int) (0.5 * (fromQ.numMsg - toQ.numMsg)));
                    fromQ.numMsg -= fromQ.lastTransferOut;
                    toQ.lastTransferIn = fromQ.lastTransferOut;
                    toQ.numMsg += toQ.lastTransferIn;
                }
            }
        }
    }

    public static class SimpleQueue {

        public String name = null;
        public int numMsg = 0;
        public int sendRate = 0;
        public int receiveRate = 0;

        public int lastTransferIn = 0;
        public int lastTransferOut = 0;
        private int lastSend = 0;
        private int lastReceive = 0;

        private int jitter(int rate) {
            int jitter = rand.nextInt((int) (deviationPct * rate + 1));
            if (rand.nextDouble() <= 0.5) {
                jitter = -1 * jitter;
            }
            return rate + (int) jitter;
        }

        public void iterate() {
            lastSend = jitter(sendRate);
            numMsg += lastSend;
            lastReceive = jitter(receiveRate);
            if (numMsg < lastReceive) {
                lastReceive = numMsg;
            }
            numMsg -= lastReceive;
        }

        public void log(int ts) {
            logger.info("event=" + name + " num_msg=" + numMsg + " last_send=" + lastSend + " last_receive="
                    + lastReceive + " change_rate=" + (lastSend / (lastReceive + 1.0)) + " send_rate=" + sendRate
                    + " receiveRate=" + receiveRate + " ts=" + ts + " t_in=" + lastTransferIn + " t_out="
                    + lastTransferOut);
            if (logs.containsKey(name + "_depth")) {
                logs.get(name + "_depth").add((double) numMsg);
            }
            if (logs.containsKey(name + "_lastsend")) {
                logs.get(name + "_lastsend").add((double) lastSend);
            }
            if (logs.containsKey(name + "_lastreceive")) {
                logs.get(name + "_lastreceive").add((double) lastReceive);
            }
            if (logs.containsKey(name + "_tout")) {
                logs.get(name + "_tout").add((double) lastTransferOut);
            }
            if (logs.containsKey(name + "_rate")) {
                logs.get(name + "_rate").add(lastSend / (lastReceive + 1.0));
            }
            if (name.equals("qA")) {
                allAOut += lastTransferOut;
            }
            if (name.equals("qB")) {
                allBOut += lastTransferOut;
            }
        }
    }

    private static void addEvent(int ts, Event e) {
        if (!events.containsKey(ts)) {
            events.put(ts, new ArrayList<Event>());
        }
        events.get(ts).add(e);
    }

    public static void simulate() {
        for (; currentTime <= duration; currentTime++) {
            List<Event> l = events.get(currentTime);
            if (l != null) {
                for (Event e : l) {
                    e.q.sendRate += e.sendRateChange;
                    e.q.receiveRate += e.receiveRateChange;
                }
            }
            if (queues.size() == 2) {
                if (rand.nextDouble() <= transferFrequency) {
                    transferStrategy.transfer(queues.get(0), queues.get(1));
                } else if (rand.nextDouble() <= transferFrequency) {
                    transferStrategy.transfer(queues.get(1), queues.get(0));
                } else {
                    queues.get(0).lastTransferIn = 0;
                    queues.get(0).lastTransferOut = 0;
                    queues.get(1).lastTransferIn = 0;
                    queues.get(1).lastTransferOut = 0;
                }
            }
            for (SimpleQueue q : queues) {
                q.iterate();
                q.log(currentTime);
            }
        }
    }

    public static void scatterPlot(Map<String, List<Double>> datasets, String filename, String title, String labelX,
            String labelY) throws IOException {
        XYSeriesCollection dataset = new XYSeriesCollection();
        for (String label : datasets.keySet()) {
            XYSeries data = new XYSeries(label);
            int cnt = 0;
            for (Double d : datasets.get(label)) {
                data.add(cnt, d);
                cnt++;
            }
            dataset.addSeries(data);
        }
        JFreeChart chart = ChartFactory.createScatterPlot(title, labelX, labelY, dataset, PlotOrientation.VERTICAL,
                true, true, false);
        ChartUtilities.saveChartAsPNG(new File(filename), chart, 1024, 768);
    }

    public static void plotLineChart(Map<String, List<Double>> series, String filename, String title, String labelX,
            String labelY) throws IOException {
        XYSeriesCollection dataset = new XYSeriesCollection();
        for (String label : series.keySet()) {
            XYSeries data = new XYSeries(label);
            int t = 0;
            for (Double d : series.get(label)) {
                data.add(t, d);
                t++;
            }
            dataset.addSeries(data);
        }
        JFreeChart chart = ChartFactory.createXYLineChart(title, labelX, labelY, dataset, PlotOrientation.VERTICAL,
                true, true, false);
        XYPlot plot = chart.getXYPlot();
        XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
        renderer.setSeriesLinesVisible(0, true);
        renderer.setSeriesShapesVisible(0, true);
        plot.setRenderer(renderer);
        ChartUtilities.saveChartAsPNG(new File(filename), chart, 1024, 768);
    }

    private static void configureQueueLogging(String queueName) {
        //logs.put(queueName + "_depth", new ArrayList<Double>());
        logs.put(queueName + "_lastsend", new ArrayList<Double>());
        logs.put(queueName + "_lastreceive", new ArrayList<Double>());
        logs.put(queueName + "_tout", new ArrayList<Double>());
        //logs.put(queueName + "_rate", new ArrayList<Double>());
    }

    public static void setupSingleDCSpikyQueue() {

        SimpleQueue qA = new SimpleQueue();
        qA.name = "qA";
        qA.sendRate = 1000;
        qA.receiveRate = 10000;

        queues.add(qA);

        configureQueueLogging(qA.name);

        Event e1 = new Event(qA, 20000, 0);
        addEvent(1000, e1);
        Event e2 = new Event(qA, -20000, 0);
        addEvent(1020, e2);

        Event e3 = new Event(qA, 20000, 0);
        addEvent(2000, e3);
        Event e4 = new Event(qA, -20000, 0);
        addEvent(2020, e4);

        Event e5 = new Event(qA, 20000, 0);
        addEvent(3000, e5);
        Event e6 = new Event(qA, -20000, 0);
        addEvent(3020, e6);
    }

    public static void setupSingleDCQueueAtCapacity() {

        SimpleQueue qA = new SimpleQueue();
        qA.name = "qA";
        qA.sendRate = 1000;
        qA.receiveRate = 1010;

        queues.add(qA);

        configureQueueLogging(qA.name);
    }

    public static void setupDualDCQueueAtCapacity() {

        SimpleQueue qA = new SimpleQueue();
        qA.name = "qA";
        qA.sendRate = 1000;
        qA.receiveRate = 1010;

        queues.add(qA);

        configureQueueLogging(qA.name);

        SimpleQueue qB = new SimpleQueue();
        qB.name = "qB";
        qB.sendRate = 1000;
        qB.receiveRate = 1010;

        queues.add(qB);

        configureQueueLogging(qB.name);
    }

    public static void setupDualDCQueueDiminishedCapacity() {

        SimpleQueue qA = new SimpleQueue();
        qA.name = "qA";
        qA.sendRate = 1000;
        qA.receiveRate = 1500;

        queues.add(qA);

        configureQueueLogging(qA.name);

        SimpleQueue qB = new SimpleQueue();
        qB.name = "qB";
        qB.sendRate = 1000;
        qB.receiveRate = 1500;

        queues.add(qB);

        configureQueueLogging(qB.name);

        Event e1 = new Event(qA, 0, -750);
        addEvent(1000, e1);
        Event e2 = new Event(qA, 0, 750);
        addEvent(2000, e2);
    }

    public static void setupDualDCQueueFullFailover() {

        SimpleQueue qA = new SimpleQueue();
        qA.name = "qA";
        qA.sendRate = 1000;
        qA.receiveRate = 1500;

        queues.add(qA);

        configureQueueLogging(qA.name);

        SimpleQueue qB = new SimpleQueue();
        qB.name = "qB";
        qB.sendRate = 1000;
        qB.receiveRate = 1500;

        queues.add(qB);

        configureQueueLogging(qB.name);

        Event e1 = new Event(qA, 0, -1500);
        addEvent(1000, e1);
    }

    public static void setupDualDCSpikyQueue() {

        SimpleQueue qA = new SimpleQueue();
        qA.name = "qA";
        qA.sendRate = 1000;
        qA.receiveRate = 10000;

        queues.add(qA);

        //configureQueueLogging(qA.name);

        Event e1 = new Event(qA, 20000, 0);
        addEvent(1000, e1);
        Event e2 = new Event(qA, -20000, 0);
        addEvent(1020, e2);

        Event e3 = new Event(qA, 20000, 0);
        addEvent(2000, e3);
        Event e4 = new Event(qA, -20000, 0);
        addEvent(2020, e4);

        Event e5 = new Event(qA, 20000, 0);
        addEvent(3000, e5);
        Event e6 = new Event(qA, -20000, 0);
        addEvent(3020, e6);

        SimpleQueue qB = new SimpleQueue();
        qB.name = "qB";
        qB.sendRate = 1000;
        qB.receiveRate = 10000;

        queues.add(qB);

        configureQueueLogging(qB.name);

        Event e7 = new Event(qB, 20000, 0);
        addEvent(1000, e7);
        Event e8 = new Event(qB, -20000, 0);
        addEvent(1020, e8);

        Event e9 = new Event(qB, 20000, 0);
        addEvent(2000, e9);
        Event e10 = new Event(qB, -20000, 0);
        addEvent(2020, e10);

        Event e11 = new Event(qB, 20000, 0);
        addEvent(3000, e11);
        Event e12 = new Event(qB, -20000, 0);
        addEvent(3020, e12);
    }

    public static void main(String argv[]) {

        try {

            Util.initLog4jTest();

            logger.info("event=simulator_started");
            transferStrategy = new DepthOnlyTransferStrategy();
            setupDualDCQueueAtCapacity();
            simulate();
            plotLineChart(logs, "/tmp/qsim_atcap_qdepth.png", "Queue Sim At Capacity Queue Depth Strategy", "ts",
                    "count");
            logger.info("event=done all_out_a=" + allAOut + " all_out_b=" + allBOut);
            clear();

            logger.info("event=simulator_started");
            transferStrategy = new DepthOnlyTransferStrategy();
            setupDualDCQueueDiminishedCapacity();
            simulate();
            plotLineChart(logs, "/tmp/qsim_dimcap_qdepth.png", "Queue Sim Diminshed Capacity Queue Depth Strategy",
                    "ts", "count");
            logger.info("event=done all_out_a=" + allAOut + " all_out_b=" + allBOut);
            clear();

            logger.info("event=simulator_started");
            transferStrategy = new DepthOnlyTransferStrategy();
            setupDualDCQueueFullFailover();
            simulate();
            plotLineChart(logs, "/tmp/qsim_fail_qdepth.png", "Queue Sim Failover Queue Depth Strategy", "ts",
                    "count");
            logger.info("event=done all_out_a=" + allAOut + " all_out_b=" + allBOut);
            clear();

            logger.info("event=simulator_started");
            transferStrategy = new DepthAndRateStrategy();
            setupDualDCQueueAtCapacity();
            simulate();
            plotLineChart(logs, "/tmp/qsim_atcap_qdepthandrate.png",
                    "Queue Sim At Capacity Queue Depth and Rate Strategy", "ts", "count");
            logger.info("event=done all_out_a=" + allAOut + " all_out_b=" + allBOut);
            clear();

            logger.info("event=simulator_started");
            transferStrategy = new DepthAndRateStrategy();
            setupDualDCQueueDiminishedCapacity();
            simulate();
            plotLineChart(logs, "/tmp/qsim_dimcap_qdepthandrate.png",
                    "Queue Sim Diminshed Capacity Queue Depth and Rate Strategy", "ts", "count");
            logger.info("event=done all_out_a=" + allAOut + " all_out_b=" + allBOut);
            clear();

            logger.info("event=simulator_started");
            transferStrategy = new DepthAndRateStrategy();
            setupDualDCQueueFullFailover();
            simulate();
            plotLineChart(logs, "/tmp/qsim_fail_qdepthandrate.png",
                    "Queue Sim Failover Queue Depth and Rate Strategy", "ts", "count");
            logger.info("event=done all_out_a=" + allAOut + " all_out_b=" + allBOut);
            clear();

        } catch (Exception ex) {
            logger.error("", ex);
        }
    }
}