All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.voltcore.zk.ZooKeeperLock Maven / Gradle / Ivy

There is a newer version: 10.1.1
Show newest version
/* This file is part of VoltDB.
 * Copyright (C) 2008-2018 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 .
 */
package org.voltcore.zk;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.apache.zookeeper_voltpatches.AsyncCallback.VoidCallback;
import org.apache.zookeeper_voltpatches.CreateMode;
import org.apache.zookeeper_voltpatches.KeeperException;
import org.apache.zookeeper_voltpatches.WatchedEvent;
import org.apache.zookeeper_voltpatches.Watcher;
import org.apache.zookeeper_voltpatches.ZooDefs.Ids;
import org.voltcore.logging.VoltLogger;

import org.apache.zookeeper_voltpatches.ZooKeeper;

//Use the mechanism similar to leader election by creating sequential ephemeral nodes.
//The node with lowest sequence number will be first granted the lock, exit the protocol.
//Then the node with next lowest sequence number gets the lock.
public class ZooKeeperLock implements Watcher {
    private static final VoltLogger hostLog = new VoltLogger("HOST");
    private final ZooKeeper m_zk;
    private final String m_basePath;
    private final String m_lockPath;
    private String m_currentPath = null;
    private final Object m_lock = new Object();

    public ZooKeeperLock(ZooKeeper zk, String nodePath, String lockName) {
        m_zk = zk;
        m_basePath = nodePath;
        m_lockPath = ZKUtil.joinZKPath(m_basePath, lockName);
    }

    /**
    * @return true if a lock is successfully acquired
    */
    public boolean acquireLock() {
        return acquireLockWithTimeout(0);
    }

    /**
     * @param timeout The value of timeout in millisecond. if timeout < 0, no timeout
     * @return true if a lock is successfully acquired
     */
    public boolean acquireLockWithTimeout(long timeout) {
        try {
            //Create a sequential node, example: /db/action_lock/lock0000000000
            m_currentPath = m_zk.create(m_lockPath, null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            synchronized(m_lock) {
                long startTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
                while(true) {
                    List nodes = getAllLockingNodes();

                    //Sort the nodes by name which ends with a 10 digit number
                    //Grant the lock to the first entry with the lowest sequence number
                    Collections.sort(nodes);
                    if (nodes.isEmpty() || m_currentPath.endsWith(nodes.get(0))) {
                        return true;
                    } else {
                        //does not get the lock this time. Wait for next update upon node deletion or addition if not timeout
                        if (timeout > 0) {
                            long nextWaitingTime = timeout - (TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - startTime);
                            if (nextWaitingTime <= 0) {
                                return false;
                            }
                            m_lock.wait(nextWaitingTime);
                        } else {
                            m_lock.wait();
                        }
                    }
                }
            }
        } catch (InterruptedException | KeeperException e) {
            hostLog.warn("Could not acquire a ZK lock:" + e.getMessage());
        }
        return false;
    }

    private List getAllLockingNodes() throws KeeperException, InterruptedException {
        //ensure that any updates with the lower sequence numbers are committed on both leader
        //and all followers to avoid any missing reads on followers because ZooKeeper does not guarantee a write
        //is immediately visible on all followers.
        CountDownLatch latch = new CountDownLatch(1);
        m_zk.sync(m_basePath, new VoidCallback() {
            @Override
            public void processResult(int rc, String path, Object ctx) {
                latch.countDown();
            }
        }, null);
        latch.await();

        //get a list of sequential nodes and watch on updates on this node
        return m_zk.getChildren(m_basePath, this);
    }

    public void releaseLock() throws IOException {
        if (m_currentPath != null) {
            try {
                m_zk.delete(m_currentPath, -1);
                m_currentPath = null;
            } catch (KeeperException | InterruptedException e) {
                throw new IOException(e);
            }
        }
    }

    @Override
    public void process(WatchedEvent event) {
        //Invoked from a separate callback thread
        //Wake up the acquireLock() thread to check if it is its turn to own the lock.
        synchronized (m_lock) {
            m_lock.notifyAll();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy