/* * Copyright 2014 University of Southern California * * 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 edu.usc.pgroup.floe.flake.messaging.dispersion.elasticmapreducer; import edu.usc.pgroup.floe.app.Tuple; import edu.usc.pgroup.floe.flake.messaging.dispersion .FlakeLocalDispersionStrategy; import org.apache.curator.framework.recipes.cache.PathChildrenCache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.SortedMap; import java.util.TreeMap; /** * @author kumbhare */ public class ElasticReducerFlakeLocalDispersion extends FlakeLocalDispersionStrategy { /** * the global logger instance. */ private static final Logger LOGGER = LoggerFactory.getLogger(ElasticReducerFlakeLocalDispersion.class); /** * Default constructor. */ public ElasticReducerFlakeLocalDispersion() { } /** * Constructor. * @param metricRegistry Metrics registry used to log various metrics. * @param context shared ZMQ context. * @param flakeId Current flake id. * public ElasticReducerFlakeLocalDispersion(final MetricRegistry metricRegistry, final ZMQ.Context context, final String flakeId) { super(metricRegistry, context, flakeId); }*/ /** * Sends the tuple to the appropriate pellet. * @param from the middleend socket to retrieve the message * @param to the backend (PUB) socket to send it to (one or more) pellets. * @Override public final void sendToPellets(final ZMQ.Socket from, final ZMQ.Socket to) { String hashInt = from.recvStr(Charset.defaultCharset()); LOGGER.info("HASH RECEIVED:{}", hashInt); Integer actualHash = Integer.parseInt(hashInt); String peinstanceid = getTargetPelletInstances(actualHash); if (peinstanceid != null) { to.sendMore(peinstanceid); Utils.forwardCompleteMessage(from, to); } else { Utils.recvAndignore(from); } }*/ /** * Returns the list of target instances to send the given tuple using the * defined strategy. * * @param tuple tuple object. * @param args custom arguments sent by the source flake with the tuple. * @return the list of target instances to send the given tuple. */ @Override public final String getTargetPelletInstance( final Tuple tuple, final List<String> args) { String hashInt = args.get(0); LOGGER.info("HASH RECEIVED:{}", hashInt); Integer actualHash = Integer.parseInt(hashInt); return getTargetPelletInstances(actualHash); } /** * The hash ring for consistent hashing. */ private SortedMap<Integer, String> circle; /** * Reverse map which stores the mapping from the flakeid to its current * value. */ private HashMap<String, Integer> reverseMap; /** * List of target pellet instances. */ private List<String> targetPelletIds; /** * Hash function to be used. */ private HashingFunction hashingFunction; /** * Path cache to monitor the tokens. */ private PathChildrenCache flakeCache; /** * Initializes the strategy. * @param args the arguments sent by the user. Fix Me: make this a better * interface. */ @Override public final void initialize( final String args) { this.targetPelletIds = new ArrayList<>(); this.circle = new TreeMap<>(Collections.reverseOrder()); this.reverseMap = new HashMap<>(); this.hashingFunction = new Murmur32(); } /** * Returns the list of target instances to send the given tuple using the * defined strategy. * @param actualHash the actual hash of the tuple key. * @return the list of target instances to send the given tuple. */ public final String getTargetPelletInstances(final Integer actualHash) { if (circle.isEmpty()) { return null; } int hash = actualHash; if (!circle.containsKey(hash)) { SortedMap<Integer, String> tailMap = circle.tailMap(hash); if (tailMap.isEmpty()) { hash = circle.firstKey(); } else { hash = tailMap.firstKey(); } } LOGGER.debug("LOCAL Key:{}, actualHash:{}, token:{}, target:{}", actualHash, hash, circle.get(hash)); return circle.get(hash); } /** * @return the current backchannel data (e.g. for loadbalancing or the * token on the ring etc.) * @Override public final byte[] getCurrentBackchannelData() { LOGGER.debug("Token: {}", getToken()); return Utils.serialize(getToken()); }*/ /** * Called whenever a new pellet is added. * * @param pelletId pellet instance id which has been added. */ @Override public final void pelletAdded(final String pelletId) { LOGGER.info("Adding :{} to circle", pelletId); Integer pelletPosition = hashingFunction.hash(pelletId.getBytes()); reverseMap.put(pelletId, pelletPosition); circle.put(pelletPosition, pelletId); LOGGER.info("Added. Circle: {}", circle); } /** * Called whenever a pellet is removed. * * @param pelletId pellet instance id which has been added. */ @Override public final void pelletRemoved(final String pelletId) { Integer key = reverseMap.get(pelletId); circle.remove(key); LOGGER.info("Removed. Circle: {}", circle); } }