Пример #1
0
 def __init__(self, cluster, nodes):
     """
     Initializes the client
     """
     cleaned_nodes = {}
     for node, info in nodes.iteritems():
         cleaned_nodes[str(node)] = ([str(entry) for entry in info[0]], int(info[1]))
     self._config = ArakoonClientConfig(str(cluster), cleaned_nodes)
     self._client = ArakoonClient(self._config)
     self._identifier = int(round(random.random() * 10000000))
     self._lock = Lock()
     self._batch_size = 500
     self._sequences = {}
Пример #2
0
class PyrakoonClient(object):
    """
    Arakoon client wrapper:
    * Uses json serialisation
    * Raises generic exception
    """
    _logger = LogHandler.get('extensions', name='pyrakoon_client')

    def __init__(self, cluster, nodes):
        """
        Initializes the client
        """
        cleaned_nodes = {}
        for node, info in nodes.iteritems():
            cleaned_nodes[str(node)] = ([str(entry) for entry in info[0]], int(info[1]))
        self._config = ArakoonClientConfig(str(cluster), cleaned_nodes)
        self._client = ArakoonClient(self._config)
        self._identifier = int(round(random.random() * 10000000))
        self._lock = Lock()
        self._batch_size = 500
        self._sequences = {}

    @locked()
    def get(self, key, consistency=None):
        """
        Retrieves a certain value for a given key
        """
        return PyrakoonClient._try(self._identifier, self._client.get, key, consistency)

    @locked()
    def get_multi(self, keys):
        """
        Get multiple keys at once
        """
        for item in PyrakoonClient._try(self._identifier, self._client.multiGet, keys):
            yield item

    @locked()
    def set(self, key, value, transaction=None):
        """
        Sets the value for a key to a given value
        """
        if transaction is not None:
            return self._sequences[transaction].addSet(key, value)
        return PyrakoonClient._try(self._identifier, self._client.set, key, value,)

    @locked()
    def prefix(self, prefix):
        """
        Lists all keys starting with the given prefix
        """
        next_prefix = PyrakoonClient._next_key(prefix)
        batch = None
        while batch is None or len(batch) > 0:
            batch = PyrakoonClient._try(self._identifier,
                                        self._client.range,
                                        beginKey=prefix if batch is None else batch[-1],
                                        beginKeyIncluded=batch is None,
                                        endKey=next_prefix,
                                        endKeyIncluded=False,
                                        maxElements=self._batch_size)
            for item in batch:
                yield item

    @locked()
    def prefix_entries(self, prefix):
        """
        Lists all keys starting with the given prefix
        """
        next_prefix = PyrakoonClient._next_key(prefix)
        batch = None
        while batch is None or len(batch) > 0:
            batch = PyrakoonClient._try(self._identifier,
                                        self._client.range_entries,
                                        beginKey=prefix if batch is None else batch[-1][0],
                                        beginKeyIncluded=batch is None,
                                        endKey=next_prefix,
                                        endKeyIncluded=False,
                                        maxElements=self._batch_size)
            for item in batch:
                yield item

    @locked()
    def delete(self, key, must_exist=True, transaction=None):
        """
        Deletes a given key from the store
        """
        if transaction is not None:
            if must_exist is True:
                return self._sequences[transaction].addDelete(key)
            else:
                return self._sequences[transaction].addReplace(key, None)
        if must_exist is True:
            return PyrakoonClient._try(self._identifier, self._client.delete, key)
        else:
            return PyrakoonClient._try(self._identifier, self._client.replace, key, None)

    @locked()
    def delete_prefix(self, prefix):
        """
        Removes a given prefix from the store
        """
        return PyrakoonClient._try(self._identifier, self._client.deletePrefix, prefix)

    @locked()
    def nop(self):
        """
        Executes a nop command
        """
        return PyrakoonClient._try(self._identifier, self._client.nop)

    @locked()
    def exists(self, key):
        """
        Check if key exists
        """
        return PyrakoonClient._try(self._identifier, self._client.exists, key)

    @locked()
    def assert_value(self, key, value, transaction=None):
        """
        Asserts a key-value pair
        """
        if transaction is not None:
            return self._sequences[transaction].addAssert(key, value)
        return PyrakoonClient._try(self._identifier, self._client.aSSert, key, value)

    @locked()
    def assert_exists(self, key, transaction=None):
        """
        Asserts that a given key exists
        """
        if transaction is not None:
            return self._sequences[transaction].addAssertExists(key)
        return PyrakoonClient._try(self._identifier, self._client.aSSert_exists, key)

    def begin_transaction(self):
        """
        Creates a transaction (wrapper around Arakoon sequences)
        """
        key = str(uuid.uuid4())
        self._sequences[key] = self._client.makeSequence()
        return key

    def apply_transaction(self, transaction):
        """
        Applies a transaction
        """
        return PyrakoonClient._try(self._identifier, self._client.sequence, self._sequences[transaction])

    @staticmethod
    def _try(identifier, method, *args, **kwargs):
        """
        Tries to call a given method, retry-ing if Arakoon is temporary unavailable
        """
        try:
            start = time.time()
            try:
                return_value = method(*args, **kwargs)
            except (ArakoonSockNotReadable, ArakoonSockReadNoBytes, ArakoonSockSendError):
                PyrakoonClient._logger.debug('Error during arakoon call {0}, retry'.format(method.__name__))
                time.sleep(1)
                return_value = method(*args, **kwargs)
            duration = time.time() - start
            if duration > 0.5:
                PyrakoonClient._logger.warning('Arakoon call {0} took {1}s'.format(method.__name__, round(duration, 2)))
            return return_value
        except (ArakoonNotFound, ArakoonAssertionFailed):
            # No extra logging for some errors
            raise
        except Exception:
            PyrakoonClient._logger.error('Error during {0}. Process {1}, thread {2}, clientid {3}'.format(
                method.__name__, os.getpid(), current_thread().ident, identifier
            ))
            raise

    @staticmethod
    def _next_key(key):
        """
        Calculates the next key (to be used in range queries)
        """
        encoding = 'ascii'  # For future python 3 compatibility
        array = bytearray(str(key), encoding)
        for index in range(len(array) - 1, -1, -1):
            array[index] += 1
            if array[index] < 128:
                while array[-1] == 0:
                    array = array[:-1]
                return str(array.decode(encoding))
            array[index] = 0
        return '\xff'