/******************************************************************************* * Copyright (c) 2013 Imperial College London. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Raul Castro Fernandez - initial design and implementation * Martin Rouaux - Changes to support scale-in of operators ******************************************************************************/ package uk.ac.imperial.lsds.seep.comm.routing; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.zip.CRC32; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.ac.imperial.lsds.seep.comm.serialization.DataTuple; import uk.ac.imperial.lsds.seep.operator.Operator; import uk.ac.imperial.lsds.seep.operator.OperatorContext; import uk.ac.imperial.lsds.seep.operator.StatefulOperator; import uk.ac.imperial.lsds.seep.operator.StatelessOperator; public class Router implements Serializable{ final private Logger LOG = LoggerFactory.getLogger(Router.class); private static final long serialVersionUID = 1L; private static final int DEFAULT_SPLIT_WINDOW = 1; //This structure rests here in case there is just one type of downstream, in case routeInfo is empty // change this for a final int position in downstreamRoutingImpl ?? private static final int INDEX_FOR_ROUTING_IMPL = 0; private static CRC32 crc32 = new CRC32(); //This map stores static info (for different types of downstream operators). StreamId -> list of downstreams private boolean requiresLogicalRouting = false; private HashMap<Integer, ArrayList<Integer>> routeInfo = new HashMap<Integer, ArrayList<Integer>>(); //This map stores the load balancer for each type of downstream. This map is related to routeInfo private HashMap<Integer, RoutingStrategyI> downstreamRoutingImpl = new HashMap<Integer, RoutingStrategyI>(); public enum RelationalOperator{ //LEQ, L, EQ, G, GEQ, RANGE EQ } public Router(boolean requiresLogicalRouting, HashMap<Integer, ArrayList<Integer>> routeInfo){ this.requiresLogicalRouting = requiresLogicalRouting; this.routeInfo = routeInfo; } //Gather indexes from statefulDynamic Load balancer public ArrayList<Integer> getIndexesInformation(int oldOpId){ RoutingStrategyI rs = null; if(!requiresLogicalRouting){ rs = downstreamRoutingImpl.get(INDEX_FOR_ROUTING_IMPL); return ((StatefulRoutingImpl)rs).getKeyToDownstreamRealIndex(); } rs = downstreamRoutingImpl.get(oldOpId); return ((StatefulRoutingImpl)rs).getKeyToDownstreamRealIndex(); } //Gather keys from statefulDynamic Load balancer public ArrayList<Integer> getKeysInformation(int oldOpId){ RoutingStrategyI rs = null; if(!requiresLogicalRouting){ rs = downstreamRoutingImpl.get(INDEX_FOR_ROUTING_IMPL); return ((StatefulRoutingImpl)rs).getDownstreamNodeKeys(); } rs = downstreamRoutingImpl.get(oldOpId); return ((StatefulRoutingImpl)rs).getDownstreamNodeKeys(); } public void configureRoutingImpl(OperatorContext opContext, ArrayList<Operator> downstream){ RoutingStrategyI rs = null; LOG.debug("-> Original downstream size: {}", downstream.size()); // For each original downstream for(Integer id : opContext.getOriginalDownstream()){ Operator down = null; for(Operator o : downstream){ if(o.getOperatorId() == id){ down = o; } } // Get index of downstream int index = opContext.getDownOpIndexFromOpId(id); if(down.getOperatorCode() instanceof StatelessOperator){ int numDownstreams = downstream.size(); LOG.debug("Configuring Static Stateless Routing Impl with {} downstreams", numDownstreams); // At this point there can only be 1 downstream (an operator can have N downstream types, //but only 1 instance of a given type Ni) rs = new StatelessRoutingImpl(DEFAULT_SPLIT_WINDOW, index, 1); } else if(down.getOperatorCode() instanceof StatefulOperator){ //We crash the stateful RI temporarily, anyway it will be recovered by the RI message rs = new StatefulRoutingImpl(index); } //If more than one downstream type, then put the new rs with the opId if(opContext.getOriginalDownstream().size() > 1){ LOG.debug("-> More than one downstream type. Assign RS for op: {}",id); downstreamRoutingImpl.put(id, rs); } //Otherwise, store the rs in the reserved place of downstreamRoutingImpl //else if (opContext.downstreams.size() == 1){ else if(opContext.getOriginalDownstream().size() == 1){ downstreamRoutingImpl.put(INDEX_FOR_ROUTING_IMPL, rs); } } } public ArrayList<Integer> routeToAll(ArrayList<Integer> logicalTargets){ ArrayList<Integer> targets = new ArrayList<Integer>(); for(Integer lt : logicalTargets){ targets = downstreamRoutingImpl.get(lt).routeToAll(targets); } return targets; } public ArrayList<Integer> forwardToAllDownstream(DataTuple dt){ ArrayList<Integer> targets = new ArrayList<Integer>(); if(requiresLogicalRouting){ ArrayList<Integer> logicalTargets = logicalRouting(dt, -1); targets = routeToAll(logicalTargets); } else{ targets = downstreamRoutingImpl.get(INDEX_FOR_ROUTING_IMPL).routeToAll(); } return targets; } public ArrayList<Integer> forwardToAllOpsInStreamId(DataTuple dt, int streamId){ ArrayList<Integer> targets = new ArrayList<Integer>(); ArrayList<Integer> logicalTargets = logicalRouting(dt, streamId); targets = routeToAll(logicalTargets); return targets; } public ArrayList<Integer> forward(DataTuple dt){ int value = 0; if(downstreamRoutingImpl == null){ System.out.println("downstreamrouting impl null"); System.exit(0); /// xtreme } if(downstreamRoutingImpl.get(INDEX_FOR_ROUTING_IMPL) == null){ for(Integer i : downstreamRoutingImpl.keySet()){ System.out.println(downstreamRoutingImpl.get(i)); } System.out.println("idx for routing impl"); System.exit(0); // xtreme } return downstreamRoutingImpl.get(INDEX_FOR_ROUTING_IMPL).route(value); } public ArrayList<Integer> forward_toOp(DataTuple dt, int streamId){ ArrayList<Integer> targets = new ArrayList<Integer>(); ArrayList<Integer> logicalTargets = logicalRouting(dt, streamId); targets = physicalRouting(logicalTargets, streamId); return targets; } public ArrayList<Integer> forward_splitKey(DataTuple dt, int key){ return downstreamRoutingImpl.get(INDEX_FOR_ROUTING_IMPL).route(key); } public ArrayList<Integer> forward_toOp_splitKey(DataTuple dt, int streamId, int key){ ArrayList<Integer> targets = new ArrayList<Integer>(); ArrayList<Integer> logicalTargets = logicalRouting(dt, streamId); targets = physicalRouting(logicalTargets, key); return targets; } private ArrayList<Integer> logicalRouting(DataTuple dt, int streamId){ return routeInfo.get(streamId); } private ArrayList<Integer> physicalRouting(ArrayList<Integer> logicalTargets, int key){ ArrayList<Integer> targets = new ArrayList<Integer>(); for(Integer ltarget : logicalTargets){ targets = downstreamRoutingImpl.get(ltarget).route(targets, key); } return targets; } public int[] newStaticOperatorPartition(int oldOpId, int newOpId, int oldOpIndex, int newOpIndex){ int key[]; if(requiresLogicalRouting){ return configureRoutingStrategyForNewPartition(oldOpId, newOpId, oldOpIndex, newOpIndex); } else{ //Otherwise, we use the default RoutingImpl key = downstreamRoutingImpl.get(INDEX_FOR_ROUTING_IMPL).newStaticReplica(oldOpIndex, newOpIndex); } return key; } public int[] newOperatorPartition(int oldOpId, int newOpId, int oldOpIndex, int newOpIndex){ int key[]; if(requiresLogicalRouting){ return configureRoutingStrategyForNewPartition(oldOpId, newOpId, oldOpIndex, newOpIndex); } else{ //Otherwise, we use the default RoutingImpl key = downstreamRoutingImpl.get(INDEX_FOR_ROUTING_IMPL).newReplica(oldOpIndex, newOpIndex); } return key; } public int[] collapseOperatorPartition(int operatorId, int operatorIndex) { int key[]; key = downstreamRoutingImpl.get(INDEX_FOR_ROUTING_IMPL).collapseReplica(operatorIndex); return key; } /** this can be moved along with downTypeToLoadBalancer */ private int[] configureRoutingStrategyForNewPartition(int oldOpId, int newOpId, int oldOpIndex, int newOpIndex) { //We gather the load balancer for the operator splitting (the old one) RoutingStrategyI rs = downstreamRoutingImpl.get(oldOpId); if(rs == null){ System.out.println("LB for OP: "+oldOpId+" is null !!!!!!!!!!"); System.out.println("OPIds: "+downstreamRoutingImpl.keySet()); } //Then we update this load balancer (the old one) with the new information int key[] = rs.newReplica(oldOpIndex, newOpIndex); //Now since we have a new replica, we want to assign the same load balancer to this replica so that it has the same route information LOG.debug("-> Registering NEW LB for OP: {}",newOpId); downstreamRoutingImpl.put(newOpId, rs); //And finally we return the new key computed return key; } private void setNewLoadBalancer(int opId, RoutingStrategyI rs){ if(requiresLogicalRouting){ downstreamRoutingImpl.put(opId, rs); } else{ downstreamRoutingImpl.put(INDEX_FOR_ROUTING_IMPL, rs); } } public void reconfigureRoutingInformation(ArrayList<Integer> downstreamIds, ArrayList<Integer> indexes, ArrayList<Integer> keys) { for(Integer opId : downstreamIds){ StatefulRoutingImpl sr = new StatefulRoutingImpl(indexes, keys); setNewLoadBalancer(opId, sr); } } public static int customHash(int value){ crc32.update(value); int v = (int)crc32.getValue(); crc32.reset(); return v; } public static int customHash(String value){ /// \todo{Search for a more efficient way of hashing a java string} int v = 0; v = value.hashCode(); return customHash(v); } } //@Deprecated //public ArrayList<Integer> forward(DataTuple dt, int value, boolean now){ // ArrayList<Integer> targets = new ArrayList<Integer>(); // // // Check if i have data to query data. branches // // //If it is necessary to query data to guess (logic)downstream // if(requiresQueryData){ // ArrayList<Integer> logicalTargets = logicalRouting(dt, value); // targets = physicalRouting(logicalTargets, value); // } // else{ // targets = downstreamRoutingImpl.get(INDEX_FOR_ROUTING_IMPL).route(value); // } // return targets; //} //if less or greater is than a given value. if equal could be with many values, with range is a special case as well //public void routeValueToDownstream(RelationalOperator operator, int value, int downstream){ // //if it is operator EQUALS, use specific routeInfo // if(operator.equals(RelationalOperator.EQ)){ // //If there was a downstream assigned for this value // if(routeInfo.containsKey(value)){ // // add the new downstream // routeInfo.get(value).add(downstream); // } // else{ // ArrayList<Integer> aux = new ArrayList<Integer>(); // aux.add(downstream); // routeInfo.put(value,aux); // } // } //} /** * this function must be executed when the operator is not initialized yet. at this point all it has is the main topology of the query * and so it needs just to put a routingImpl per oprator. Now, the problem is that when this is being called because of a scale out/scale in * the downstream is not the original one. NEED to differentiate between main/execution graph. Or, make explicit whith type is and call as it is required * CHANGE-> actually the DIFFERENTIATION IS required **/ /*public void _configureRoutingImpl(OperatorContext opContext){ RoutingStrategyI rs = null; int opId = 0; //For every downstream for(PlacedOperator down : opContext.downstreams){ opId = down.opID(); int index = opContext.findDownstream(opId).index(); if(!down.isStateful()){ int numDownstreams = opContext.getDownstreamSize(); rs = new StatelessRoutingImpl(1, index, numDownstreams); } else if(down.isStateful()){ rs = new StatefulRoutingImpl(index); } System.out.println("ADDED"); downstreamRoutingImpl.put(opId, rs); } NodeManager.nLogger.info("ROUTING ENGINE CONFIGURED"); }*/