/******************************************************************************* * 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 ******************************************************************************/ package uk.ac.imperial.lsds.seep.api.operator; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.ac.imperial.lsds.seep.api.QueryExecutionMode; import uk.ac.imperial.lsds.seep.api.SeepTask; import uk.ac.imperial.lsds.seep.api.operator.sinks.Sink; import uk.ac.imperial.lsds.seep.api.operator.sinks.TaggingSink; import uk.ac.imperial.lsds.seep.api.operator.sources.Source; import uk.ac.imperial.lsds.seep.api.operator.sources.TaggingSource; import uk.ac.imperial.lsds.seep.api.state.SeepState; import uk.ac.imperial.lsds.seep.errors.InvalidQueryDefinitionException; public class SeepLogicalQuery { final private static Logger LOG = LoggerFactory.getLogger(SeepLogicalQuery.class); private List<LogicalOperator> logicalOperators = new ArrayList<>(); private List<LogicalOperator> sources = new ArrayList<>(); // TODO: there could be many sinks private LogicalOperator sink; private List<SeepState> states = new ArrayList<>(); private Map<Integer, Integer> initialPhysicalInstancesPerOperator = new HashMap<>(); private QueryExecutionMode qem = QueryExecutionMode.ALL_MATERIALIZED; public void setExecutionModeHint(QueryExecutionMode qem) { if(this.validExecutionModeForThisQuery(qem)) { this.qem = qem; } else { LOG.warn("Impossible to honour the requested execution mode. Query will run with: "+qem.toString()); } } public QueryExecutionMode getQueryExecutionMode() { return qem; } private boolean validExecutionModeForThisQuery(QueryExecutionMode qem){ LOG.warn("We are not checking requested execution mode !!!"); return true; } public List<LogicalOperator> getAllOperators(){ return logicalOperators; } public LogicalOperator getOperatorWithId(int opId){ for(LogicalOperator lo : logicalOperators){ if(lo.getOperatorId() == opId) return lo; } return null; } public List<SeepState> getAllStates(){ return states; } public List<LogicalOperator> getSources(){ return sources; } public void addSource(LogicalOperator lo){ this.sources.add(lo); } public void addSink(LogicalOperator lo) { this.sink = lo; } public LogicalOperator getSink(){ return sink; } public void setInitialPhysicalInstancesPerLogicalOperator(int opId, int numInstances) { // Sanity checks if(numInstances - 1 < 1){ throw new InvalidQueryDefinitionException("Minimum num Instances per logicalOperator is 1"); } if(this.initialPhysicalInstancesPerOperator.containsKey(opId)){ throw new InvalidQueryDefinitionException("Illegal action. Can set up numInstances only once"); } // Operator to statically scale out LogicalOperator lo = null; for(Operator o : logicalOperators){ if(o.getOperatorId() == opId) lo = (LogicalOperator)o; } if(lo == null){ throw new InvalidQueryDefinitionException("Impossible to set num instances for NON-EXISTENT op: "+opId); } if(lo instanceof TaggingSource || lo instanceof TaggingSink) { throw new InvalidQueryDefinitionException("Impossible to scale out a predefined Source or Sink"); } // Create scale out and update numInstances per op for(int instance = 0; instance < (numInstances-1); instance++) { // with 1 instance, we don't need to do anything int instanceOpId = getNewOpIdForInstance(opId, instance); LogicalOperator newInstance = null; SeepTask st = lo.getSeepTask(); SeepState s = lo.getState(); if(lo.isStateful()){ if(st instanceof Source){ newInstance = this.newStatefulSource(st, s, instanceOpId); } else if(st instanceof Sink) { newInstance = this.newStatefulSink(st, s, instanceOpId); } else { newInstance = this.newStatefulOperator(st, s, instanceOpId); } } else { if(st instanceof Source){ newInstance = this.newStatelessSource(st, instanceOpId); } else if(st instanceof Sink) { newInstance = this.newStatelessSink(st, instanceOpId); } else { newInstance = this.newStatelessOperator(st, instanceOpId); } } connectInstance(lo, newInstance); } this.initialPhysicalInstancesPerOperator.put(opId, numInstances); } private void connectInstance(LogicalOperator original, LogicalOperator newInstance){ for(DownstreamConnection dc : original.downstreamConnections()){ newInstance.connectTo(dc.getDownstreamOperator(), dc.getStreamId(), dc.getExpectedDataStoreOfDownstream(), dc.getConnectionType()); } for(UpstreamConnection uc : original.upstreamConnections()){ uc.getUpstreamOperator().connectTo(newInstance, uc.getStreamId(), uc.getDataStore(), uc.getConnectionType()); } } // TODO: there are better ways to do this... private int getNewOpIdForInstance(int opId, int it){ return opId * 1000 + (it+100); } public int getInitialPhysicalInstancesForLogicalOperator(int opId){ if (initialPhysicalInstancesPerOperator.containsKey(opId)) return initialPhysicalInstancesPerOperator.get(opId); else return 1; // there is always, at least one instance per defined operator } public boolean hasSetInitialPhysicalInstances(int opId){ return initialPhysicalInstancesPerOperator.containsKey(opId); } public LogicalOperator newStatefulSource(SeepTask seepTask, SeepState state, int opId){ LogicalOperator lo = newStatefulOperator(seepTask, state, opId); this.sources.add(lo); return lo; } public LogicalOperator newStatelessSource(SeepTask seepTask, int opId){ LogicalOperator lo = newStatelessOperator(seepTask, opId); this.sources.add(lo); return lo; } public LogicalOperator newStatefulOperator(SeepTask seepTask, SeepState state, int opId){ if(state == null) { throw new InvalidQueryDefinitionException("State cannot be null"); } state.setOwner(opId); LogicalOperator lo = SeepLogicalOperator.newStatefulOperator(opId, seepTask, state); logicalOperators.add(lo); states.add(state); return lo; } public LogicalOperator newStatelessOperator(SeepTask seepTask, int opId){ LogicalOperator lo = SeepLogicalOperator.newStatelessOperator(opId, seepTask); logicalOperators.add(lo); return lo; } public LogicalOperator newStatefulSink(SeepTask seepTask, SeepState state, int opId){ LogicalOperator lo = newStatefulOperator(seepTask, state, opId); this.sink = lo; return lo; } public LogicalOperator newStatelessSink(SeepTask seepTask, int opId){ LogicalOperator lo = newStatelessOperator(seepTask, opId); this.sink = lo; return lo; } @Override public String toString(){ String ls = System.getProperty("line.separator"); StringBuilder sb = new StringBuilder(); sb.append("Seep Query"); sb.append(ls); sb.append("##########"); sb.append(ls); sb.append("#Sources: "+this.sources.size()); sb.append(ls); sb.append("#Operators(including-sources): "+this.logicalOperators.size()); sb.append(ls); sb.append("#States: "+this.states.size()); sb.append(ls); return sb.toString(); } }