/* This file is part of VoltDB. * Copyright (C) 2008-2017 VoltDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with VoltDB. If not, see <http://www.gnu.org/licenses/>. */ package org.voltcore.zk; import java.nio.ByteBuffer; import java.util.LinkedList; import org.apache.zookeeper_voltpatches.CreateMode; import org.apache.zookeeper_voltpatches.KeeperException; import org.apache.zookeeper_voltpatches.ZooDefs.Ids; import org.apache.zookeeper_voltpatches.ZooKeeper; import org.apache.zookeeper_voltpatches.data.Stat; /** * CoreZK provides constants for all voltcore-registered * ZooKeeper paths. */ public class CoreZK { private static final String root = "/core"; // unique instance ID for this cluster, // which is the JSON object: // -- coord: (int) original coordinator IP address as an int // -- timestamp: (long) the timestamp at which the first zookeeper node was created public static final String instance_id = "/core/instance_id"; // hosts in the current system (ephemeral) public static final String hosts = "/core/hosts"; public static final String hosts_host = "/core/hosts/host"; public static final String readyhosts = "/core/readyhosts"; public static final String readyhosts_host = "/core/readyhosts/host"; public static final String readyjoininghosts = "/core/readyjoininghosts"; // hosts since beginning of time (persistent) public static final String hostids = "/core/hostids"; public static final String hostids_host = "/core/hostids/host"; // root for rejoin nodes public static final String rejoin_node_blocker = "/core/rejoin_nodes_blocker"; // Persistent nodes (mostly directories) to create on startup public static final String[] ZK_HIERARCHY = { root, hosts, readyhosts, readyjoininghosts, hostids }; /** * Creates the ZK directory nodes. Only the leader should do this. */ public static void createHierarchy(ZooKeeper zk) { LinkedList<ZKUtil.StringCallback> callbacks = new LinkedList<ZKUtil.StringCallback>(); for (String node : CoreZK.ZK_HIERARCHY) { ZKUtil.StringCallback cb = new ZKUtil.StringCallback(); callbacks.add(cb); zk.create(node, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, cb, null); } try { for (ZKUtil.StringCallback cb : callbacks) { cb.get(); } } catch (Exception e) { org.voltdb.VoltDB.crashLocalVoltDB( e.getMessage(), false, e); } } /** * Given a ZK node name of the form PREFIX_SUFFIX (for example, the * format used by the LeaderElector which is HSID_SEQUENCENUM), return * the prefix. The prefix cannot have any underscores in it. */ public static String getPrefixFromChildName(String childName) { return childName.split("_")[0]; } /** * Given a ZK node name of the form PREFIX_SUFFIX (for example, the * format used by the LeaderElector which is HSID_SEQUENCENUM), return * the suffix. The suffix cannot have any underscores in it. */ public static String getSuffixFromChildName(String childName) { final String[] parts = childName.split("_"); return parts[parts.length - 1]; } /** * Creates a rejoin blocker for the given rejoining host. * This prevents other hosts from rejoining at the same time. * * @param zk ZooKeeper client * @param hostId The rejoining host ID * @return -1 if the blocker is created successfully, or the host ID * if there is already another host rejoining. */ public static int createRejoinNodeIndicator(ZooKeeper zk, int hostId) { try { zk.create(rejoin_node_blocker, ByteBuffer.allocate(4).putInt(hostId).array(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch (KeeperException e) { if (e.code() == KeeperException.Code.NODEEXISTS) { try { return ByteBuffer.wrap(zk.getData(rejoin_node_blocker, false, null)).getInt(); } catch (KeeperException e1) { if (e1.code() != KeeperException.Code.NONODE) { org.voltdb.VoltDB.crashLocalVoltDB("Unable to get the current rejoining node indicator"); } } catch (InterruptedException e1) {} } else { org.voltdb.VoltDB.crashLocalVoltDB("Unable to create rejoin node Indicator", true, e); } } catch (InterruptedException e) { org.voltdb.VoltDB.crashLocalVoltDB("Unable to create rejoin node Indicator", true, e); } return -1; } /** * Removes the rejoin blocker if the current rejoin blocker contains the given host ID. * @return true if the blocker is removed successfully, false otherwise. */ public static boolean removeRejoinNodeIndicatorForHost(ZooKeeper zk, int hostId) { try { Stat stat = new Stat(); final int rejoiningHost = ByteBuffer.wrap(zk.getData(rejoin_node_blocker, false, stat)).getInt(); if (hostId == rejoiningHost) { zk.delete(rejoin_node_blocker, stat.getVersion()); return true; } } catch (KeeperException e) { if (e.code() == KeeperException.Code.NONODE || e.code() == KeeperException.Code.BADVERSION) { // Okay if the rejoin blocker for the given hostId is already gone. return true; } } catch (InterruptedException e) { return false; } return false; } /** * Removes the join indicator for the given host ID. * @return true if the indicator is removed successfully, false otherwise. */ public static boolean removeJoinNodeIndicatorForHost(ZooKeeper zk, int hostId) { try { Stat stat = new Stat(); String path = ZKUtil.joinZKPath(readyjoininghosts, Integer.toString(hostId)); zk.getData(path, false, stat); zk.delete(path, stat.getVersion()); return true; } catch (KeeperException e) { if (e.code() == KeeperException.Code.NONODE || e.code() == KeeperException.Code.BADVERSION) { // Okay if the join indicator for the given hostId is already gone. return true; } } catch (InterruptedException e) { return false; } return false; } }