/******************************************************************************* * 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 - support for generic scaling rules ******************************************************************************/ package uk.ac.imperial.lsds.seep.api; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.ac.imperial.lsds.seep.infrastructure.master.Node; import uk.ac.imperial.lsds.seep.infrastructure.monitor.policy.PolicyRules; import uk.ac.imperial.lsds.seep.operator.Connectable; import uk.ac.imperial.lsds.seep.operator.Operator; import uk.ac.imperial.lsds.seep.operator.OperatorCode; import uk.ac.imperial.lsds.seep.operator.compose.MultiOperator; import uk.ac.imperial.lsds.seep.operator.compose.SubOperator; import uk.ac.imperial.lsds.seep.state.CustomState; import uk.ac.imperial.lsds.seep.state.LargeState; import uk.ac.imperial.lsds.seep.state.Partitionable; import uk.ac.imperial.lsds.seep.state.StateWrapper; public class QueryPlan { final private Logger LOG = LoggerFactory.getLogger(QueryPlan.class); private int nodeId = 0; private ArrayList<Operator> ops = new ArrayList<Operator>(); private ArrayList<StateWrapper> states = new ArrayList<StateWrapper>(); private ArrayList<ScaleOutIntentBean> scIntents = new ArrayList<ScaleOutIntentBean>(); private Map<Operator, Integer> partitionRequirements = new LinkedHashMap<Operator, Integer>(0); private Map<Integer, Connectable> elements = new HashMap<Integer, Connectable>(); //More than one source is supported private ArrayList<Operator> src = new ArrayList<Operator>(); private Operator snk; //Mapping of operators to node private Map<Integer, Operator> mapOperatorToNode = new LinkedHashMap<Integer, Operator>(); private PolicyRules policyRules; public ArrayList<Operator> getOps(){ return ops; } public ArrayList<StateWrapper> getStates(){ return states; } public ArrayList<ScaleOutIntentBean> getScaleOutIntents(){ return scIntents; } public Map<Integer, Connectable> getElements() { return elements; } public Map<Operator, Integer> getPartitionRequirements(){ return partitionRequirements; } public ArrayList<Operator> getSrc() { return src; } public Operator getSnk() { return snk; } public Map<Integer, Operator> getMapOperatorToNode(){ return mapOperatorToNode; } /** User facing methods **/ public Operator newStatefulSource(OperatorCode opCode, int opId, StateWrapper s, List<String> attributes){ // Configure operator if(s.getOwnerId() != opId){ LOG.error("Operator id: "+opId+" does not own state: "+s.getOwnerId()); System.exit(0); } Operator op = this.newStatefulOperator(opCode, opId, s, attributes); this.setSource(op); op.getOpContext().setIsSource(true); return op; } public Operator newStatelessSource(OperatorCode opCode, int opId, List<String> attributes){ // Configure operator Operator op = this.newStatelessOperator(opCode, opId, attributes); this.setSource(op); op.getOpContext().setIsSource(true); return op; } public Operator newStatefulSink(OperatorCode opCode, int opId, StateWrapper s, List<String> attributes){ // Configure operator if(s.getOwnerId() != opId){ LOG.error("Operator id: "+opId+" does not own state: "+s.getOwnerId()); System.exit(0); } Operator op = this.newStatefulOperator(opCode, opId, s, attributes); this.setSink(op); op.getOpContext().setIsSink(true); return op; } public Operator newStatelessSink(OperatorCode opCode, int opId, List<String> attributes){ Operator op = this.newStatelessOperator(opCode, opId, attributes); this.setSink(op); op.getOpContext().setIsSink(true); return op; } public Operator newStatelessOperator(OperatorCode opCode, int opId, List<String> attributes){ // Configure operator Operator op = Operator.getStatelessOperator(opId, opCode, attributes); this.addOperator(op); try { this.place(op); } catch (NodeAlreadyInUseException e) { LOG.error("The instantiation has tried to place an operator in an already used node. Is queryBuilder used by multiple threads?"); e.printStackTrace(); } return op; } public Operator newStatefulOperator(OperatorCode opCode, int opId, StateWrapper s, List<String> attributes){ // Configure operator if(s.getOwnerId() != opId){ LOG.error("Operator id: "+opId+" does not own state: "+s.getOwnerId()); System.exit(0); } Operator op = Operator.getStatefulOperator(opId, opCode, s, attributes); // Register state this.registerState(s); this.addOperator(op); try { this.place(op); } catch (NodeAlreadyInUseException e) { LOG.error("The instantiation has tried to place an operator in an already used node. Is queryBuilder used by multiple threads?"); e.printStackTrace(); } return op; } public Connectable newMultiOperator(Set<SubOperator> subOperators, int multiOpId, List<String> attributes) { // First create multiOperator MultiOperator mo = MultiOperator.synthesizeFrom(subOperators, multiOpId); // Then compose the multiOperator into a SEEP Operator Operator op = Operator.getStatelessOperator(multiOpId, mo, attributes); this.addOperator(op); try { this.place(op); } catch (NodeAlreadyInUseException e) { LOG.error("The instantiation has tried to place an operator in an already used node. Is queryBuilder used by multiple threads?"); e.printStackTrace(); } return op; } public StateWrapper newCustomState(CustomState s, int ownerId, int checkpointInterval, String keyAttribute){ StateWrapper sw = new StateWrapper(ownerId, checkpointInterval, s); if(s instanceof Partitionable){ ((Partitionable)s).setKeyAttribute(keyAttribute); } else{ // TODO: say that keyattribute is ignored as state does not implement Partitionable } return sw; } public StateWrapper newLargeState(LargeState s, int ownerId, int checkpointInterval){ return new StateWrapper(ownerId, checkpointInterval, s); } /** Static scaling methods **/ /** This is the preferred function, that will automatically load balance the static partitioning**/ public void scaleOut(int opId, int numPartitions){ Operator op = getOperatorWithOpId(opId); partitionRequirements.put(op, numPartitions); } /** This function is provided in case the user wants to manually define which partitions to be done**/ public void scaleOut(int opId, int newOpId, Node newProvisionedNode){ Operator op = getOperatorWithOpId(opId); // Register the intent to scale out ScaleOutIntentBean soib = new ScaleOutIntentBean(op, newOpId, newProvisionedNode); scIntents.add(soib); } private Operator getOperatorWithOpId(int opId){ for(Operator op : ops){ if(op.getOperatorId() == opId) return op; } return null; } /** Mapping between operator and node. Note that only one operator is assigned to one node. Two "conceptual" operators can be * merged into one "deployable" operator. This means that at this point all optimisations regarding "conceptual" operators have * been made. * @throws NodeAlreadyInUseException **/ public void place(Operator o) throws NodeAlreadyInUseException{ if(mapOperatorToNode.containsKey(nodeId)){ throw new NodeAlreadyInUseException(); } else{ mapOperatorToNode.put(nodeId, o); } // Finally we increase the nodeId nodeId++; } private void setSource(Operator source) { LOG.info("Configured NEW SOURCE, Operator: {}", src.toString()); src.add(source); } private void setSink(Operator snk){ LOG.info("Configured SINK as Operator: {}", snk.toString()); this.snk = snk; } private void registerState(StateWrapper s){ states.add(s); LOG.debug("Added new State to Query"); } private void addOperator(Operator o) { ops.add(o); elements.put(o.getOperatorId(), o); LOG.info("Added new Operator to Infrastructure: {}", o.toString()); } public void withPolicyRules(PolicyRules rules) { policyRules = rules; LOG.info("Initialising query with scaling policy: {}", rules.toString()); } public PolicyRules getPolicyRules() { return policyRules; } }