def release(self):
        """Release a lock

        :returns: True if the lock was released, or False if it is no
                  longer valid.
        :rtype: bool

        """
        self._revoked = []
        try:
            safe_call(self._zk, 'delete', self._candidate_path)
            return True
        except (zookeeper.NoNodeException, AttributeError):
            return False
Exemple #2
0
    def release(self):
        """Release a lock

        :returns: True if the lock was released, or False if it is no
                  longer valid.
        :rtype: bool

        """
        self._revoked = []
        try:
            safe_call(self._zk, 'delete', self._candidate_path)
            return True
        except (zookeeper.NoNodeException, AttributeError):
            return False
Exemple #3
0
    def clear(self):
        """Clear out a lock

        .. warning::

            You must be sure this is a dead lock, as clearing it will
            forcibly release it by deleting all lock nodes.

        :returns: True if the lock was cleared, or False if it
                  is no longer valid.
        :rtype: bool

        """
        children = safe_call(self._zk, 'get_children', self._locknode)
        for child in children:
            try:
                safe_call(self._zk, 'delete', self._locknode + '/' + child)
            except zookeeper.NoNodeException:
                pass
Exemple #4
0
    def _ensure_lock_dir(self):
        # Ensure our lock dir exists
        if safe_call(self._zk, 'exists', self._locknode):
            return

        try:
            safe_call(self._zk, 'create', self._lock_root,
                      "zktools ZLock dir", [ZOO_OPEN_ACL_UNSAFE], 0)
        except zookeeper.NodeExistsException:
            if self._log_debug:
                log.debug("Lock node in Zookeeper already created")

        # Try and create our locking node
        try:
            safe_call(self._zk, 'create', self._locknode, "lock",
                      [ZOO_OPEN_ACL_UNSAFE], 0)
        except zookeeper.NodeExistsException:
            # Ok if this exists already
            pass
    def clear(self):
        """Clear out a lock

        .. warning::

            You must be sure this is a dead lock, as clearing it will
            forcibly release it by deleting all lock nodes.

        :returns: True if the lock was cleared, or False if it
                  is no longer valid.
        :rtype: bool

        """
        children = safe_call(self._zk, 'get_children', self._locknode)
        for child in children:
            try:
                safe_call(self._zk, 'delete', self._locknode + '/' + child)
            except zookeeper.NoNodeException:
                pass
    def _ensure_lock_dir(self):
        # Ensure our lock dir exists
        if safe_call(self._zk, 'exists', self._locknode):
            return

        try:
            safe_call(self._zk, 'create', self._lock_root, "zktools ZLock dir",
                      [ZOO_OPEN_ACL_UNSAFE], 0)
        except zookeeper.NodeExistsException:
            if self._log_debug:
                log.debug("Lock node in Zookeeper already created")

        # Try and create our locking node
        try:
            safe_call(self._zk, 'create', self._locknode, "lock",
                      [ZOO_OPEN_ACL_UNSAFE], 0)
        except zookeeper.NodeExistsException:
            # Ok if this exists already
            pass
Exemple #7
0
 def revoke_watcher(handle, type, state, path):
     # This method must be in closure scope to ensure that
     # it can append to the thread it is called from
     # to indicate if this particular thread's lock was
     # revoked or removed
     if type == zookeeper.CHANGED_EVENT:
         data = safe_call(self._zk, 'get', path, revoke_watcher)[0]
         if data == 'unlock':
             self._revoked.append(True)
     elif type == zookeeper.DELETED_EVENT or \
          state == zookeeper.EXPIRED_SESSION_STATE:
         # Trigger if node was deleted
         self._revoked.append(True)
 def revoke_watcher(handle, type, state, path):
     # This method must be in closure scope to ensure that
     # it can append to the thread it is called from
     # to indicate if this particular thread's lock was
     # revoked or removed
     if type == zookeeper.CHANGED_EVENT:
         data = safe_call(self._zk, 'get', path, revoke_watcher)[0]
         if data == 'unlock':
             self._revoked.append(True)
     elif type == zookeeper.DELETED_EVENT or \
          state == zookeeper.EXPIRED_SESSION_STATE:
         # Trigger if node was deleted
         self._revoked.append(True)
Exemple #9
0
    def revoke_all(self):
        """Revoke any existing locks, gently

        Unlike :meth:`clear`, this asks all existing locks to
        release, rather than forcibly revoking them.

        :returns: True if existing locks were present, False if
                  there were no existing locks.
        :rtype: bool

        """
        # Get all the children of the node
        children = safe_call(self._zk, 'get_children', self._locknode)
        if not children:
            return False

        for child in children:
            try:
                safe_call(self._zk, 'set', self._locknode + '/' + child,
                          "unlock")
            except zookeeper.NoNodeException:
                pass
        return True
    def revoke_all(self):
        """Revoke any existing locks, gently

        Unlike :meth:`clear`, this asks all existing locks to
        release, rather than forcibly revoking them.

        :returns: True if existing locks were present, False if
                  there were no existing locks.
        :rtype: bool

        """
        # Get all the children of the node
        children = safe_call(self._zk, 'get_children', self._locknode)
        if not children:
            return False

        for child in children:
            try:
                safe_call(self._zk, 'set', self._locknode + '/' + child,
                          "unlock")
            except zookeeper.NoNodeException:
                pass
        return True
Exemple #11
0
    def __init__(self, connection, lock_name, lock_root='/ZktoolsLocks'):
        """Create an Asynchronous Zookeeper Lock

        :param connection: Zookeeper connection object
        :type connection: zc.zk Zookeeper instance
        :param lock_name: Path to the lock node that should be used
        :param lock_root: Path to the root lock node to create the locks
                          under
        :type lock_root: string

        """
        self._zk = connection
        self._lock_path = '/'.join([lock_root, lock_name])
        self._lock_event = threading.Event()
        self._acquired = False
        self._candidate_path = None
        self._acquire_func = self._release_func = None
        self.errors = []

        try:
            safe_call(self._zk, 'create_recursive', self._lock_path,
                      "zktools ZLock dir", [ZOO_OPEN_ACL_UNSAFE])
        except zookeeper.NodeExistsException:
            pass
    def __init__(self, connection, lock_name, lock_root='/ZktoolsLocks'):
        """Create an Asynchronous Zookeeper Lock

        :param connection: Zookeeper connection object
        :type connection: zc.zk Zookeeper instance
        :param lock_name: Path to the lock node that should be used
        :param lock_root: Path to the root lock node to create the locks
                          under
        :type lock_root: string

        """
        self._zk = connection
        self._lock_path = '/'.join([lock_root, lock_name])
        self._lock_event = threading.Event()
        self._acquired = False
        self._candidate_path = None
        self._acquire_func = self._release_func = None
        self.errors = []

        try:
            safe_call(self._zk, 'create_recursive', self._lock_path,
                      "zktools ZLock dir", [ZOO_OPEN_ACL_UNSAFE])
        except zookeeper.NodeExistsException:
            pass
Exemple #13
0
    def has_lock(self):
        """Check with Zookeeper to see if the lock is acquired

        :returns: Whether the lock is acquired or not
        :rtype: bool

        """
        if not self._candidate_path:
            # So we can check it even if we released
            return False

        znode = self._candidate_path
        keyname = znode[znode.rfind('/') + 1:]
        # Get all the children of the node
        children = safe_call(self._zk, 'get_children', self._locknode)
        children.sort(key=lambda val: val[val.rfind('-') + 1:])
        if keyname not in children:
            return False

        acquired = self._has_lock(keyname, children)[0]
        return bool(acquired)
    def has_lock(self):
        """Check with Zookeeper to see if the lock is acquired

        :returns: Whether the lock is acquired or not
        :rtype: bool

        """
        if not self._candidate_path:
            # So we can check it even if we released
            return False

        znode = self._candidate_path
        keyname = znode[znode.rfind('/') + 1:]
        # Get all the children of the node
        children = safe_call(self._zk, 'get_children', self._locknode)
        children.sort(key=lambda val: val[val.rfind('-') + 1:])
        if keyname not in children:
            return False

        acquired = self._has_lock(keyname, children)[0]
        return bool(acquired)
Exemple #15
0
    def _acquire_lock(self, node_name, timeout=None, revoke=False):
        """Acquire a lock

        Internal function used by read/write lock

        :param node_name: Name of the node to use for the lock
        :type node_name: str
        :param timeout: How long to wait to acquire the lock, set to 0 to
                        get non-blocking behavior.
        :type timeout: int
        :param revoke: Whether prior locks should be revoked. Can be set to
                       True to request and wait for prior locks to release
                       their lock, or :obj:`IMMEDIATE` to destroy the blocking
                       read/write locks and attempt to acquire a write lock.
        :type revoke: bool or :obj:``IMMEDIATE``


        :returns: True if the lock was acquired, False otherwise
        :rtype: bool

        """
        # First clear out any prior revocation warnings
        self._revoked = []

        # Create a lock node
        self._candidate_path = znode = safe_create_ephemeral_sequence(
            self._zk, self._locknode + node_name, "0", [ZOO_OPEN_ACL_UNSAFE])

        @threaded
        def revoke_watcher(handle, type, state, path):
            # This method must be in closure scope to ensure that
            # it can append to the thread it is called from
            # to indicate if this particular thread's lock was
            # revoked or removed
            if type == zookeeper.CHANGED_EVENT:
                data = safe_call(self._zk, 'get', path, revoke_watcher)[0]
                if data == 'unlock':
                    self._revoked.append(True)
            elif type == zookeeper.DELETED_EVENT or \
                 state == zookeeper.EXPIRED_SESSION_STATE:
                # Trigger if node was deleted
                self._revoked.append(True)

        data = safe_call(self._zk, 'get', znode, revoke_watcher)[0]
        if data == 'unlock':
            self._revoked.append(True)
        keyname = znode[znode.rfind('/') + 1:]

        acquired = False
        cv = threading.Event()

        def lock_watcher(handle, type, state, path):
            cv.set()

        lock_start = time.time()
        first_run = True
        while not acquired:
            cv.clear()

            # Have we been at this longer than the timeout?
            if not first_run:
                if timeout is not None and time.time() - lock_start > timeout:
                    try:
                        safe_call(self._zk, 'delete', znode)
                    except zookeeper.NoNodeException:
                        pass
                    return False
            first_run = False

            # Get all the children of the node
            children = safe_call(self._zk, 'get_children', self._locknode)
            children.sort(key=lambda val: val[val.rfind('-') + 1:])

            if len(children) == 0 or not keyname in children:
                # Disconnects or other errors can cause this
                self._candidate_path = znode = safe_create_ephemeral_sequence(
                    self._zk, self._locknode + node_name, "0",
                    [ZOO_OPEN_ACL_UNSAFE])
                keyname = znode[znode.rfind('/') + 1:]
                data = safe_call(self._zk, 'get', znode, revoke_watcher)[0]
                if data == 'unlock':
                    self._revoked.append(True)
                continue

            acquired, blocking_nodes = self._has_lock(keyname, children)
            if acquired:
                break

            if revoke == IMMEDIATE:
                # Remove all prior nodes
                for node in blocking_nodes:
                    try:
                        safe_call(self._zk, 'delete',
                                  self._locknode + '/' + node)
                    except zookeeper.NoNodeException:
                        pass
                continue  # Now try again
            elif revoke:
                # Ask all prior blocking nodes to release
                for node in blocking_nodes:
                    try:
                        safe_call(self._zk, 'set',
                                  self._locknode + '/' + node, "unlock")
                    except zookeeper.NoNodeException:
                        pass

            prior_blocking_node = self._locknode + '/' + blocking_nodes[-1]
            exists = safe_call(self._zk, 'exists', prior_blocking_node,
                               lock_watcher)
            if not exists:
                # The node disappeared? Rinse and repeat.
                continue

            # Wait for a notification from get_children, no longer
            # than the timeout
            wait_for = None
            if timeout is not None:
                time_spent = time.time() - lock_start
                wait_for = timeout - time_spent
            cv.wait(wait_for)
        return True
    def _acquire_lock(self, node_name, timeout=None, revoke=False):
        """Acquire a lock

        Internal function used by read/write lock

        :param node_name: Name of the node to use for the lock
        :type node_name: str
        :param timeout: How long to wait to acquire the lock, set to 0 to
                        get non-blocking behavior.
        :type timeout: int
        :param revoke: Whether prior locks should be revoked. Can be set to
                       True to request and wait for prior locks to release
                       their lock, or :obj:`IMMEDIATE` to destroy the blocking
                       read/write locks and attempt to acquire a write lock.
        :type revoke: bool or :obj:``IMMEDIATE``


        :returns: True if the lock was acquired, False otherwise
        :rtype: bool

        """
        # First clear out any prior revocation warnings
        self._revoked = []

        # Create a lock node
        self._candidate_path = znode = safe_create_ephemeral_sequence(
            self._zk, self._locknode + node_name, "0", [ZOO_OPEN_ACL_UNSAFE])

        @threaded
        def revoke_watcher(handle, type, state, path):
            # This method must be in closure scope to ensure that
            # it can append to the thread it is called from
            # to indicate if this particular thread's lock was
            # revoked or removed
            if type == zookeeper.CHANGED_EVENT:
                data = safe_call(self._zk, 'get', path, revoke_watcher)[0]
                if data == 'unlock':
                    self._revoked.append(True)
            elif type == zookeeper.DELETED_EVENT or \
                 state == zookeeper.EXPIRED_SESSION_STATE:
                # Trigger if node was deleted
                self._revoked.append(True)

        data = safe_call(self._zk, 'get', znode, revoke_watcher)[0]
        if data == 'unlock':
            self._revoked.append(True)
        keyname = znode[znode.rfind('/') + 1:]

        acquired = False
        cv = threading.Event()

        def lock_watcher(handle, type, state, path):
            cv.set()

        lock_start = time.time()
        first_run = True
        while not acquired:
            cv.clear()

            # Have we been at this longer than the timeout?
            if not first_run:
                if timeout is not None and time.time() - lock_start > timeout:
                    try:
                        safe_call(self._zk, 'delete', znode)
                    except zookeeper.NoNodeException:
                        pass
                    return False
            first_run = False

            # Get all the children of the node
            children = safe_call(self._zk, 'get_children', self._locknode)
            children.sort(key=lambda val: val[val.rfind('-') + 1:])

            if len(children) == 0 or not keyname in children:
                # Disconnects or other errors can cause this
                self._candidate_path = znode = safe_create_ephemeral_sequence(
                    self._zk, self._locknode + node_name, "0",
                    [ZOO_OPEN_ACL_UNSAFE])
                keyname = znode[znode.rfind('/') + 1:]
                data = safe_call(self._zk, 'get', znode, revoke_watcher)[0]
                if data == 'unlock':
                    self._revoked.append(True)
                continue

            acquired, blocking_nodes = self._has_lock(keyname, children)
            if acquired:
                break

            if revoke == IMMEDIATE:
                # Remove all prior nodes
                for node in blocking_nodes:
                    try:
                        safe_call(self._zk, 'delete',
                                  self._locknode + '/' + node)
                    except zookeeper.NoNodeException:
                        pass
                continue  # Now try again
            elif revoke:
                # Ask all prior blocking nodes to release
                for node in blocking_nodes:
                    try:
                        safe_call(self._zk, 'set', self._locknode + '/' + node,
                                  "unlock")
                    except zookeeper.NoNodeException:
                        pass

            prior_blocking_node = self._locknode + '/' + blocking_nodes[-1]
            exists = safe_call(self._zk, 'exists', prior_blocking_node,
                               lock_watcher)
            if not exists:
                # The node disappeared? Rinse and repeat.
                continue

            # Wait for a notification from get_children, no longer
            # than the timeout
            wait_for = None
            if timeout is not None:
                time_spent = time.time() - lock_start
                wait_for = timeout - time_spent
            cv.wait(wait_for)
        return True