Exemplo n.º 1
0
    def val(self, default=None):
        """Get the actual value of this proxy"""
        full_key = self._get_full_key(None)

        # Check if this exact key exists. If yes, it's an scalar value.
        # This check will fail if the user set a 'None' value.
        # See below for this case.
        conn = self._conn()
        value = _to_native(conn.get(full_key))
        if value is not None:
            return json.loads(value)

        nested = {}
        for redis_key in conn.scan_iter(full_key + '.*'):
            value = _to_native(conn.get(redis_key))

            # Strip the full key prefix, we're only interested in returning
            # the children values directly.
            sub_key = _to_native(redis_key[len(full_key) + 1:])
            util.feed_to_nested(nested, sub_key, json.loads(value))

        # This is for the case that this key does not exist, or
        # for the case that the user explicitly set a None value.
        if len(nested) is 0:
            # Key really does not exist.
            if default is None and not self.exists():
                return None

            return default

        return nested
Exemplo n.º 2
0
def _get_sub_keys(conn, full_key):
    """Helper to yield all sub keys including the own key.
    Useful to

    :param conn: (ConnectionPool) Where to get the connection from.
    :param full_key: (str)
    """
    for redis_key in conn.scan_iter(full_key + ".*"):
        yield _to_native(redis_key)

    yield _to_native(full_key)
Exemplo n.º 3
0
    def iter_children(self):
        """Iterate over all children including and below this node.

        :returns: A generator object, yielding ValueProxies.
        """
        own_path = self._get_full_key(None)
        for redis_key in self._conn().scan_iter(own_path + '.*'):
            # Split the VAL_TREE_PREFIX away, it will be added again when
            # creating a new _Proxy.
            trimmed_key = _to_native(redis_key[len(LOCK_TREE_PREFIX) + 1:])
            yield _REGISTRY.proxy_from_registry(trimmed_key,
                                                db_name=self._db_name)
Exemplo n.º 4
0
    def _acquire(self, pipe, key):
        """Actual implementation of acquire(), called by _find_root_lock

        :param pipe: (redis.StrictPipe) A redis pipe.
        :param key: (str) The full key to lock.
        """
        retries = self._acquire_seconds * 20
        lock_count = 0

        own_pid = os.getpid()
        own_thread_ident = threading.current_thread().ident

        while retries > 0:
            lock_token = _to_native(pipe.get(key))

            # Does this lock even exist?
            if lock_token is None:
                break

            # Is it maybe one of our own locks?
            pid, thread_ident, lock_count = util.parse_lock_token(lock_token)
            if pid == own_pid and thread_ident == own_thread_ident:
                break

            # Cannot lock now. Wait a short time before a retry.
            time.sleep(0.05)
            retries -= 1

        if retries is 0:
            raise LockTimeout(
                "Lock timed out after {} retries".format(
                    self._acquire_seconds * 20
                )
            )

        # Take over and make sure the key expires at some time.
        # Also increment the lock count.
        pipe.multi()
        pipe.set(key, util.build_lock_token(lock_count + 1))
        if self._expire_seconds is not None:
            pipe.expire(key, self._expire_seconds)
Exemplo n.º 5
0
    def _release(self, pipe, key):
        """Actual implementation of release(), called by _find_root_lock

        :param pipe: (redis.Pipe) A redis pipe.
        :param key: (str) The full key to release.
        """
        lock_token = _to_native(pipe.get(key))
        if lock_token is None:
            # This lock token does not exist anymore. This either means
            # it expired (okay) or release was called without prior acquire
            # (which is pretty bad). We can't really tell, so just return.
            return

        # We could also check if pid and thread_ident is the same as own,
        # but that might fail on edge cases where the key expired and
        # another pid/thread got the lock (validly). We don't check here
        # since we cannot know for sure.
        _, _, lock_count = util.parse_lock_token(lock_token)

        pipe.multi()

        # These exceptions are supposed to uncover errors in locking logic.
        if lock_count <= 0:
            raise InternalError(
                "Negative or zero lock count ({}) for {} ({})".format(
                    lock_count, key, self
                )
            )

        if lock_count == 1:
            # Remove the key; we're not locking it anymore.
            pipe.delete(key)
        else:
            # We held an recursive lock. Leave our mark.
            pipe.set(key, util.build_lock_token(lock_count - 1))
            if self._expire_seconds is not None:
                pipe.expire(key, self._expire_seconds)
Exemplo n.º 6
0
 def _is_locked(pipe, key):
     """Actual implementation of is_locked"""
     lock_value = _to_native(pipe.get(key))
     return lock_value is not None