// 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.time.Duration; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import com.twitter.heron.api.generated.TopologyAPI; import com.twitter.heron.common.basics.WakeableLooper; public class XORManager { private final WakeableLooper looper; // map of task_id to a RotatingMap private final Map<Integer, RotatingMap> spoutTasksToRotatingMap; // The rotate interval in nano-seconds private final Duration rotateInterval; public XORManager(WakeableLooper looper, Duration timeout, List<Integer> taskIds, int nBuckets) { this.looper = looper; this.spoutTasksToRotatingMap = new HashMap<>(); // We would do the first rotate after timeoutSec seconds, // And then would do the rotate at every rotateIntervalNs nano-second Runnable r = new Runnable() { @Override public void run() { rotate(); } }; looper.registerTimerEvent(timeout, r); this.rotateInterval = timeout.dividedBy(nBuckets).plusNanos(timeout.getNano()); for (Integer taskId : taskIds) { spoutTasksToRotatingMap.put(taskId, new RotatingMap(nBuckets)); } } /** * Populate the XORManager for all spouts for the topology. * * @param looper The WakeableLooper to execute timer event * @param topology The given topology protobuf * @param nBuckets number of buckets to divide the message timeout seconds * @param componentToTaskIds the map of componentName to its list of taskIds in the topology * @return the XORManager for all spouts' task for the topology */ public static XORManager populateXORManager(WakeableLooper looper, TopologyAPI.Topology topology, int nBuckets, Map<String, List<Integer>> componentToTaskIds) { List<Integer> allSpoutTasks = new LinkedList<>(); // Only spouts need acking management, i.e. xor maintenance for (TopologyAPI.Spout spout : topology.getSpoutsList()) { for (TopologyAPI.OutputStream outputStream : spout.getOutputsList()) { List<Integer> spoutTaskIds = componentToTaskIds.get(outputStream.getStream().getComponentName()); allSpoutTasks.addAll(spoutTaskIds); } } return new XORManager(looper, PhysicalPlanUtil.extractTopologyTimeout(topology), allSpoutTasks, nBuckets); } // Create a new entry for the tuple. // taskId is the task id where the tuple // originated from. // key is generated in spout for rootId // value is the tuple key as seen by the // destination public void create(int taskId, long key, long value) { spoutTasksToRotatingMap.get(taskId).create(key, value); } // Add one more entry to the tuple tree // taskId is the task id where the tuple // originated from. // key is generated in spout for rootId // value is the tuple key as seen by the // destination // We return true if the xor value is now zerod out // Else return false public boolean anchor(int taskId, long key, long value) { return spoutTasksToRotatingMap.get(taskId).anchor(key, value); } // remove this tuple key from our structure. // return true if this key was found. else false public boolean remove(int taskId, long key) { return spoutTasksToRotatingMap.get(taskId).remove(key); } // Invoke the rotate methods for all RotatingMap of all spout tasks // Protected method for unit test protected void rotate() { for (RotatingMap map : spoutTasksToRotatingMap.values()) { map.rotate(); } // Plan itself in rotateIntervalNs interval Runnable r = new Runnable() { @Override public void run() { rotate(); } }; looper.registerTimerEvent(rotateInterval, r); } // For unit test protected Map<Integer, RotatingMap> getSpoutTasksToRotatingMap() { return spoutTasksToRotatingMap; } }