/* * Copyright 2009 Amit Levy, Jeff Prouty, Rylan Hawkins * * 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.washington.cs.cse490h.donut.server; import java.util.List; import java.util.logging.Logger; import org.apache.thrift.TException; import com.google.inject.Inject; import edu.washington.cs.cse490h.donut.business.Node; import edu.washington.cs.cse490h.donut.service.LocatorClientFactory; import edu.washington.cs.cse490h.donut.service.RetryFailedException; import edu.washington.cs.cse490h.donut.Constants; import edu.washington.cs.cse490h.donut.business.KeyId; import edu.washington.cs.cse490h.donut.service.NodeNotFoundException; import edu.washington.cs.cse490h.donut.business.TNode; import edu.washington.cs.cse490h.donut.service.KeyLocator.Iface; import edu.washington.cs.cse490h.donut.util.KeyIdUtil; /** * @author alevy */ public class DonutClient extends Thread { private static final Logger LOGGER; private final Node node; private final LocatorClientFactory clientFactory; private int nextFingerToUpdate; final private AbstractRunAtInterval stabilize; final private AbstractRunAtInterval checkPredecessor; final private AbstractRunAtInterval fixFingers; static { LOGGER = Logger.getLogger(DonutClient.class.getName()); } @Inject public DonutClient(Node node, LocatorClientFactory clientFactory) { this.node = node; this.clientFactory = clientFactory; nextFingerToUpdate = 0; // Initialize the worker threads stabilize = new AbstractRunAtInterval(Constants.STABILIZE_INTERVAL) { public void runClosure() { stabilize(); } }; fixFingers = new AbstractRunAtInterval(Constants.FIX_FINGERS_INTERVAL) { public void runClosure() { fixFingers(); } }; checkPredecessor = new AbstractRunAtInterval(Constants.CHECK_PREDECESSOR_INTERVAL) { public void runClosure() { checkPredecessor(); } }; } public void join(TNode n) throws TException { try { if (!n.equals(node.getTNode())) { TNode found = clientFactory.get(n).findSuccessor(node.getNodeId()); node.setSuccessor(found); clientFactory.release(n); } } catch (RetryFailedException e) { throw new TException(e); } LOGGER.info("Joined Donut [" + Node.TNodeToString(node.getTNode()) + "]: Known Node - " + Node.TNodeToString(n)); } public boolean ping(TNode n) { try { clientFactory.get(n).ping(); clientFactory.release(n); return true; } catch (RetryFailedException e) { return false; } catch (TException e) { // Thrift error. Take a look at the trace clientFactory.release(n); LOGGER.warning(e.toString()); return false; } } /** * Called periodically. Checks whether the predecessor has failed. */ public void checkPredecessor() { if (this.node.getPredecessor() != null && !ping(this.node.getPredecessor())) { // A predecessor is defined but could not be reached. Nullify the current predecessor LOGGER.warning("Lost Predecessor [" + Node.TNodeToString(node.getTNode()) + "]: Predecessor - " + Node.TNodeToString(node.getPredecessor())); this.node.setPredecessor(null); } } /** * Called periodically. Refreshes the finger table entries. nextFingerToUpdate stores the index * of the next finger to fix. */ public void fixFingers() { fixFinger(nextFingerToUpdate); nextFingerToUpdate = (nextFingerToUpdate + 1) % node.getFingers().size(); } public void fixFinger(int finger) { Iface iface; try { iface = clientFactory.get(node.getTNode()); } catch (RetryFailedException e) { // We should always be able to connect to our own service. If not, its serious LOGGER.severe("FixFingers [" + Node.TNodeToString(node.getTNode()) + "] failed to get client to itself."); // Skip this finger. It will get updated on the next go throw the finger table. return; } // Keep as separate variable: Be careful of some weird java issues with overflowing ints long base = node.getNodeId().getId(); long pow = 0x0000000000000001L << finger; long id = base + pow; KeyId keyId = new KeyId(id); try { TNode updatedFinger = iface.findSuccessor(keyId); this.node.setFinger(finger, updatedFinger); } catch (TException e1) { LOGGER.warning("Thrift Exception in findSuccessor [" + Node.TNodeToString(node.getTNode()) + "]: keyId-" + keyId); } clientFactory.release(node.getTNode()); } /** * Called periodically. Verify's immediate successor, and tell's successor about us. */ public void stabilize() { TNode x = null; TNode successor = node.getSuccessor(); Iface successorClient; try { successorClient = clientFactory.get(successor); } catch (RetryFailedException e) { LOGGER.info("Lost successor [" + Node.TNodeToString(node.getTNode()) + "]: Successor- " + Node.TNodeToString(successor)); clientFactory.release(successor); node.removeSuccessor(); return; } try { x = successorClient.getPredecessor(); } catch (NodeNotFoundException e) { // Successor's predecessor is null } catch (TException e) { LOGGER.info("Lost successor [" + Node.TNodeToString(node.getTNode()) + "]: Successor - " + Node.TNodeToString(node.getSuccessor())); node.removeSuccessor(); clientFactory.release(successor); return; } if (x != null && KeyIdUtil.isAfterXButBeforeEqualY(x.getNodeId(), node.getNodeId(), successor .getNodeId())) { clientFactory.release(successor); successor = x; try { successorClient = clientFactory.get(successor); } catch (RetryFailedException e) { LOGGER.info("Lost successor [" + Node.TNodeToString(node.getTNode()) + "]: Successor - " + Node.TNodeToString(node.getSuccessor())); clientFactory.release(successor); return; } } try { List<TNode> successorList = successorClient.notify(node.getTNode()); // Set successor *MUST* be called after notify for replication // to be guaranteed node.updateSuccessorList(successorList); node.setSuccessor(successor); } catch (TException e) { LOGGER.info("Lost successor [" + Node.TNodeToString(node.getTNode()) + "]: Successor - " + Node.TNodeToString(node.getSuccessor())); return; } finally { clientFactory.release(successor); } } /** * Stops the worker threads. */ public void kill() { stabilize.kill(); checkPredecessor.kill(); fixFingers.kill(); } /** * Starts the worker threads. */ @Override public void run() { super.run(); stabilize.start(); checkPredecessor.start(); fixFingers.start(); } }