edu.umass.cs.reconfiguration.deprecated.ReconfigurableClientCreateTester.java Source code

Java tutorial

Introduction

Here is the source code for edu.umass.cs.reconfiguration.deprecated.ReconfigurableClientCreateTester.java

Source

/*
 * Copyright (c) 2015 University of Massachusetts
 * 
 * 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.
 * 
 * Initial developer(s): V. Arun
 */
package edu.umass.cs.reconfiguration.deprecated;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.json.JSONException;
import org.json.JSONObject;

import edu.umass.cs.nio.AbstractJSONPacketDemultiplexer;
import edu.umass.cs.nio.JSONMessenger;
import edu.umass.cs.nio.JSONPacket;
import edu.umass.cs.nio.MessageNIOTransport;
import edu.umass.cs.nio.nioutils.PacketDemultiplexerDefault;
import edu.umass.cs.nio.nioutils.StringifiableDefault;
import edu.umass.cs.reconfiguration.ActiveReplica;
import edu.umass.cs.reconfiguration.ReconfigurationConfig;
import edu.umass.cs.reconfiguration.examples.AppRequest;
import edu.umass.cs.reconfiguration.reconfigurationpackets.BasicReconfigurationPacket;
import edu.umass.cs.reconfiguration.reconfigurationpackets.ClientReconfigurationPacket;
import edu.umass.cs.reconfiguration.reconfigurationpackets.CreateServiceName;
import edu.umass.cs.reconfiguration.reconfigurationpackets.DeleteServiceName;
import edu.umass.cs.reconfiguration.reconfigurationpackets.ReconfigurationPacket;
import edu.umass.cs.reconfiguration.reconfigurationpackets.ReconfigureRCNodeConfig;
import edu.umass.cs.reconfiguration.reconfigurationpackets.RequestActiveReplicas;
import edu.umass.cs.reconfiguration.reconfigurationutils.ConsistentReconfigurableNodeConfig;
import edu.umass.cs.utils.DelayProfiler;
import edu.umass.cs.utils.MyLogger;

/**
 * @author V. Arun
 * 
 *         This class is used to test batch creation of a large number of names.
 *         Individually creating names is rather slow, so batch creation helps
 *         significantly increase the creation throughput. For batch creation,
 *         all of the names in a batch must have the same set of initial active
 *         replicas.
 *         <p>
 * 
 *         Note: There is no corresponding batch deletion mechanism. In general,
 *         different names will have different sets of active replicas, so it is
 *         not meaningful to delete a batch of names as an atomic operation.
 * 
 */

public class ReconfigurableClientCreateTester {

    private final Set<InetSocketAddress> reconfigurators;
    private final JSONMessenger<?> messenger;
    private final ConcurrentHashMap<String, Long> sentRequests = new ConcurrentHashMap<String, Long>();
    private final ConcurrentHashMap<String, BasicReconfigurationPacket<?>> rcvdResponses = new ConcurrentHashMap<String, BasicReconfigurationPacket<?>>();
    private Set<InetSocketAddress> activeReplicas = null;

    private Logger log = Logger.getLogger(getClass().getName());

    ReconfigurableClientCreateTester(Set<InetSocketAddress> reconfigurators, JSONMessenger<?> messenger) {
        this.reconfigurators = reconfigurators;
        this.messenger = messenger;
        messenger.addPacketDemultiplexer(new ClientPacketDemultiplexer());
    }

    /*
     * This method makes a batched create request. The main piece of additional
     * information needed here compared to a typical single create request is
     * the set of reconfigurator node IDs as opposed to just their socket
     * addresses. We need this because we need to ensure that all creates in a
     * batch map to the same RC group. For this, we need RC IDs because IDs, not
     * socket addresses, are used for consistent-hashing RCs on to the ring.
     */
    /**
     * @param name
     * @param state
     * @param batchSize
     * @return Array of batched CreateServiceName requests.
     */
    public CreateServiceName[] makeCreateNameRequest(String name, String state, int batchSize) {
        Set<String> names = new HashSet<String>();
        for (int i = 0; i < batchSize; i++)
            names.add(name + i);
        Collection<Set<String>> batches = ConsistentReconfigurableNodeConfig.splitIntoRCGroups(names,
                ReconfigurationConfig.getReconfiguratorIDs());

        Set<CreateServiceName> creates = new HashSet<CreateServiceName>();
        // each batched create corresponds to a different RC group
        for (Set<String> batch : batches) {
            Map<String, String> nameStates = new HashMap<String, String>();
            for (String bname : batch) {
                nameStates.put(bname, state);
            }
            // a single batched create
            creates.add(new CreateServiceName(null, nameStates));
        }
        return creates.toArray(new CreateServiceName[0]);
    }

    private Set<InetSocketAddress> getReconfigurators() {
        return this.reconfigurators;
    }

    private InetSocketAddress getRandomRCReplica() {
        int index = (int) (this.getReconfigurators().size() * Math.random());
        InetSocketAddress address = (InetSocketAddress) (this.getReconfigurators().toArray()[index]);
        return new InetSocketAddress(address.getAddress(), ActiveReplica.getClientFacingPort(address.getPort()));
    }

    private InetSocketAddress getFirstRCReplica() {
        InetSocketAddress address = this.getReconfigurators().iterator().next();
        return new InetSocketAddress(address.getAddress(), ActiveReplica.getClientFacingPort(address.getPort()));
    }

    private static final boolean RANDOM_SERVER = true;

    private String sendRequest(BasicReconfigurationPacket<?> req) throws JSONException, IOException {
        InetSocketAddress sockAddr = (!RANDOM_SERVER ? this.getFirstRCReplica() : this.getRandomRCReplica());
        log.log(Level.INFO, MyLogger.FORMAT[7].replace(" ", ""),
                new Object[] { "Sending ", req.getSummary(), " to ", sockAddr, ":", (sockAddr) });
        this.sentRequests.put(req.getServiceName(), System.currentTimeMillis());
        this.sendRequest(sockAddr, req.toJSONObject());
        return req.getServiceName();
    }

    private void sendRequest(InetSocketAddress id, JSONObject json) throws JSONException, IOException {
        // modify
        this.messenger.sendToAddress(id, json);
    }

    private class ClientPacketDemultiplexer extends AbstractJSONPacketDemultiplexer {

        ClientPacketDemultiplexer() {
            this.register(ReconfigurationPacket.PacketType.CREATE_SERVICE_NAME);
            this.register(ReconfigurationPacket.PacketType.DELETE_SERVICE_NAME);
            this.register(AppRequest.PacketType.DEFAULT_APP_REQUEST);
            this.register(ReconfigurationPacket.PacketType.REQUEST_ACTIVE_REPLICAS);
            this.register(ReconfigurationPacket.PacketType.RECONFIGURE_RC_NODE_CONFIG);
        }

        @Override
        public boolean handleMessage(JSONObject json) {
            log.log(Level.FINEST, "Client received {0}", new Object[] { json });
            try {
                ReconfigurationPacket.PacketType rcType = ReconfigurationPacket.getReconfigurationPacketType(json);
                if (rcType != null) {
                    switch (ReconfigurationPacket.getReconfigurationPacketType(json)) {
                    case CREATE_SERVICE_NAME:
                        CreateServiceName create = new CreateServiceName(json);
                        log.log(Level.INFO, "Received create {0} for name {1} {2}",
                                new Object[] {
                                        (create.isFailed() ? " ***********ERROR*********** " : " CONFIRMATION "),
                                        create.getServiceName(),
                                        create.isFailed() ? ": " + create.getResponseMessage() : "" });
                        notifyReply(create);
                        break;

                    case DELETE_SERVICE_NAME:
                        DeleteServiceName delete = new DeleteServiceName(json);
                        log.log(Level.INFO, "Received delete {0} for name {1} {2}",
                                new Object[] { delete.isFailed() ? " ERROR " : " CONFIRMATION ",
                                        delete.getServiceName(),
                                        delete.isFailed() ? ": " + delete.getResponseMessage() : "" });
                        notifyReply(delete);
                        break;
                    case REQUEST_ACTIVE_REPLICAS:
                        RequestActiveReplicas reqActives = new RequestActiveReplicas(json);
                        log.log(Level.INFO, "Received active replicas for {0} : {1}",
                                new Object[] { reqActives.getServiceName(), reqActives.getActives() });
                        activeReplicas = reqActives.getActives();
                        // we want to put failed responses here in rcvsResponses
                        notifyAnyReply(reqActives);
                        break;
                    case RECONFIGURE_RC_NODE_CONFIG:
                        ReconfigureRCNodeConfig<String> rcnc = new ReconfigureRCNodeConfig<String>(json,
                                new StringifiableDefault<String>(""));
                        log.log(Level.INFO, "Received node config change {0} {1}{2}",
                                new Object[] { rcnc.isFailed() ? "ERROR: " + rcnc.getMessage() : "confirmation",
                                        (rcnc.newlyAddedNodes != null ? "; added" + rcnc.newlyAddedNodes : ""),
                                        (rcnc.deletedNodes != null ? "; deleted" + rcnc.deletedNodes : "") });
                        notifyReply(rcnc);
                        ;
                        break;

                    default:
                        break;
                    }
                }

                AppRequest.PacketType type = AppRequest.PacketType.getPacketType(JSONPacket.getPacketType(json));
                if (type != null) {
                    switch (AppRequest.PacketType.getPacketType(JSONPacket.getPacketType(json))) {
                    case DEFAULT_APP_REQUEST:
                        AppRequest request = new AppRequest(json);
                        log.log(Level.INFO, MyLogger.FORMAT[1], new Object[] { "App executed request",
                                request.getRequestID() + ":" + request.getValue() });
                        sentRequests.remove(request.getServiceName());
                        notifyReply();
                        break;
                    case ANOTHER_APP_REQUEST:
                    default:
                        throw new RuntimeException("Client received unexpected APP_COORDINATION message");
                    }
                }

            } catch (JSONException je) {
                je.printStackTrace();
            } catch (RuntimeException re) {
                re.printStackTrace();
                fatalException(re);
            }
            return true;
        }
    }

    private void fatalException(Exception e) {
        System.out.println("!!!!!FATAL exception: " + e.getMessage() + "; exiting!!!!!!");
        System.exit(1);

    }

    Set<InetSocketAddress> getActiveReplicas() {
        return this.activeReplicas;
    }

    private static final long REQUEST_TIMEOUT = 2000;
    private static final long RC_RECONFIGURE_TIMEOUT = 4000;

    synchronized BasicReconfigurationPacket<?> waitForReply(String name, long timeout) {
        while (sentRequests.containsKey(name)
        // || System.currentTimeMillis() - sentRequests.get(name) < timeout
        )
            try {
                wait(timeout);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        return rcvdResponses.remove(name);
    }

    synchronized BasicReconfigurationPacket<?> waitForReply(String name) {
        return this.waitForReply(name, REQUEST_TIMEOUT);
    }

    // only for ClientReconfigurationPacket
    synchronized boolean waitForSuccess(String name) {
        ClientReconfigurationPacket reply = (ClientReconfigurationPacket) this.waitForReply(name);
        System.out.println("unblocked from wait; reply = " + (reply != null ? reply.getSummary() : "null"));
        return reply != null && !reply.isFailed();
    }

    // only for ClientReconfigurationPacket
    synchronized boolean waitForFailure(String name) {
        ClientReconfigurationPacket reply = (ClientReconfigurationPacket) this.waitForReply(name);
        return reply != null && reply.isFailed();
    }

    synchronized boolean waitForReconfigureRCSuccess(String name) {
        BasicReconfigurationPacket<?> reply = this.waitForReply(name, RC_RECONFIGURE_TIMEOUT);
        return reply != null && reply instanceof ReconfigureRCNodeConfig<?>
                && !((ReconfigureRCNodeConfig<?>) reply).isFailed();
    }

    private static final long APP_REQUEST_TIMEOUT = 200;

    synchronized boolean rcvdAppReply(String name) {
        if (sentRequests.containsKey(name))
            try {
                wait(APP_REQUEST_TIMEOUT);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        return !sentRequests.containsKey(name);
    }

    synchronized void notifyReply(ClientReconfigurationPacket response) {
        if (!response.isFailed() && sentRequests.remove(response.getServiceName()) != null)
            rcvdResponses.put(response.getServiceName(), response);
        notify();
    }

    synchronized void notifyAnyReply(ClientReconfigurationPacket response) {
        if (sentRequests.remove(response.getServiceName()) != null)
            rcvdResponses.put(response.getServiceName(), response);
        notify();

    }

    synchronized void notifyReply() {
        notify();
    }

    synchronized void notifyReply(ReconfigureRCNodeConfig<?> response) {
        if (!response.isFailed() && sentRequests.remove(response.getServiceName()) != null)
            rcvdResponses.put(response.getServiceName(), response);
        notify();
    }

    /**
     * 
     */
    public static int TEST_PORT = 61000;

    /**
     * Simple test client for the reconfiguration package. Clients only know the
     * set of all reconfigurators, not active replicas for any name. All
     * information about active replicas for a name is obtained from
     * reconfigurators. Any request can be sent to any reconfigurator and it
     * will forward to the appropriate reconfigurator if necessary and relay
     * back the response.
     * 
     * @param args
     */
    public static void main(String[] args) {
        try {
            /*
             * Client can only send/receive clear text or do server-only
             * authentication
             */
            JSONMessenger<?> messenger = new JSONMessenger<String>((new MessageNIOTransport<String, JSONObject>(
                    null, null, new PacketDemultiplexerDefault(), true, ReconfigurationConfig.getClientSSLMode())));
            final ReconfigurableClientCreateTester client = new ReconfigurableClientCreateTester(
                    ReconfigurationConfig.getReconfiguratorAddresses(), messenger);
            String initValue = "initVal";
            int numIterations = 1;

            for (int j = 0; j < numIterations; j++) {
                String namePrefix = "name" + (int) (Math.random() * Integer.MAX_VALUE);
                long t0 = System.currentTimeMillis();

                // ///////////////// batched create name//////////////////////
                t0 = System.currentTimeMillis();
                // do
                int numCreates = 1;
                int batchSize = 50000;
                for (int i = 0; i < numCreates; i++) {
                    final int k = i;
                    try {
                        // batch size is being specified here
                        CreateServiceName[] creates = client.makeCreateNameRequest(namePrefix + k, initValue,
                                batchSize);
                        for (CreateServiceName create : creates) {
                            client.sendRequest(create);
                            System.out.println("Sent batched request of size " + create.getNameStates().size());
                            while (!client.waitForSuccess(create.getServiceName()))
                                ;
                        }
                    } catch (JSONException | IOException e) {
                        e.printStackTrace();
                    }
                    System.out.println("SUCCESS " + k);
                }
                DelayProfiler.updateDelay("createName", t0);
                System.out.println(System.currentTimeMillis() - t0);
                System.exit(1);
            }

        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }
}