// Copyright 2016 Twitter. All rights reserved. // // 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.twitter.heron.simulator.utils; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; import com.twitter.heron.api.generated.TopologyAPI; import com.twitter.heron.proto.system.HeronTuples; public class TupleCache { private final Map<Integer, TupleList> cache = new HashMap<>(); protected TupleList get(int destTaskId) { TupleList list = cache.get(destTaskId); if (list == null) { list = new TupleList(); cache.put(destTaskId, list); } return list; } public long addDataTuple(int destTaskId, TopologyAPI.StreamId streamId, HeronTuples.HeronDataTuple tuple, boolean isAnchored) { return get(destTaskId).addDataTuple(streamId, tuple, isAnchored); } public void addAckTuple(int taskId, HeronTuples.AckTuple tuple) { get(taskId).addAckTuple(tuple); } public void addFailTuple(int taskId, HeronTuples.AckTuple tuple) { get(taskId).addFailTuple(tuple); } public void addEmitTuple(int taskId, HeronTuples.AckTuple tuple) { get(taskId).addEmitTuple(tuple); } // Construct a new Map from current cache // Modification on Map would not cahnge values in cache public Map<Integer, List<HeronTuples.HeronTupleSet>> getCache() { Map<Integer, List<HeronTuples.HeronTupleSet>> res = new HashMap<>(); for (Map.Entry<Integer, TupleList> entry : cache.entrySet()) { res.put(entry.getKey(), entry.getValue().getTuplesList()); } return res; } public boolean isEmpty() { return cache.isEmpty(); } public void clear() { cache.clear(); } protected static class TupleList { private final List<HeronTuples.HeronTupleSet> tuples; private final Random random; private HeronTuples.HeronTupleSet.Builder current; public TupleList() { tuples = new LinkedList<>(); random = new Random(); } // returns the tuple key used for XOR public long addDataTuple(TopologyAPI.StreamId streamId, HeronTuples.HeronDataTuple tuple, boolean isAnchored) { if (current == null || current.hasControl() || !current.getDataBuilder().getStream().getComponentName().equals( streamId.getComponentName()) || !current.getDataBuilder().getStream().getId().equals(streamId.getId())) { if (current != null) { tuples.add(current.build()); } current = HeronTuples.HeronTupleSet.newBuilder(); current.getDataBuilder().setStream(streamId); } long tupleKey = -1; if (isAnchored) { tupleKey = random.nextLong(); current.getDataBuilder().addTuples( HeronTuples.HeronDataTuple.newBuilder().mergeFrom(tuple).setKey(tupleKey)); } else { // We don't care tuple key value current.getDataBuilder().addTuples(tuple); } return tupleKey; } public void addAckTuple(HeronTuples.AckTuple tuple) { if (current == null || current.hasData() || current.getControlBuilder().getFailsCount() > 0 || current.getControlBuilder().getEmitsCount() > 0) { if (current != null) { tuples.add(current.build()); } current = HeronTuples.HeronTupleSet.newBuilder(); } current.getControlBuilder().addAcks(tuple); } public void addFailTuple(HeronTuples.AckTuple tuple) { if (current == null || current.hasData() || current.getControlBuilder().getAcksCount() > 0 || current.getControlBuilder().getEmitsCount() > 0) { if (current != null) { tuples.add(current.build()); } current = HeronTuples.HeronTupleSet.newBuilder(); } current.getControlBuilder().addFails(tuple); } public void addEmitTuple(HeronTuples.AckTuple tuple) { if (current == null || current.hasData() || current.getControlBuilder().getAcksCount() > 0 || current.getControlBuilder().getFailsCount() > 0) { if (current != null) { tuples.add(current.build()); } current = HeronTuples.HeronTupleSet.newBuilder(); } current.getControlBuilder().addEmits(tuple); } public List<HeronTuples.HeronTupleSet> getTuplesList() { // Add current to tuple list if current is not null if (current != null) { tuples.add(current.build()); // Reset current current = null; } return tuples; } public void clear() { current = null; tuples.clear(); } } }