예제 #1
0
    def upsert_internal(self, kind, item):
        r = redis.Redis(connection_pool=self._pool)
        base_key = self._items_key(kind)
        key = item['key']
        item_json = json.dumps(item)

        while True:
            pipeline = r.pipeline()
            pipeline.watch(base_key)
            old = self.get_internal(kind, key)
            if self.test_update_hook is not None:
                self.test_update_hook(base_key, key)
            if old and old['version'] >= item['version']:
                log.debug(
                    'RedisFeatureStore: Attempted to %s key: %s version %d with a version that is the same or older: %d in "%s"',
                    'delete' if item.get('deleted') else 'update', key,
                    old['version'], item['version'], kind.namespace)
                pipeline.unwatch()
                return old
            else:
                pipeline.multi()
                pipeline.hset(base_key, key, item_json)
                try:
                    pipeline.execute()
                    # Unlike Redis implementations for other platforms, in redis-py a failed WATCH
                    # produces an exception rather than a null result from execute().
                except redis.exceptions.WatchError:
                    log.debug(
                        "RedisFeatureStore: concurrent modification detected, retrying"
                    )
                    continue
            return item
예제 #2
0
    def _update_with_versioning(self, kind, item):
        r = redis.Redis(connection_pool=self._pool)
        base_key = self._items_key(kind)
        key = item['key']
        item_json = json.dumps(item)

        while True:
            pipeline = r.pipeline()
            pipeline.watch(base_key)
            old = self._get_even_if_deleted(kind, key, check_cache=False)
            self._before_update_transaction(base_key, key)
            if old and old['version'] >= item['version']:
                log.debug('RedisFeatureStore: Attempted to %s key: %s version %d with a version that is the same or older: %d in "%s"',
                    'delete' if item.get('deleted') else 'update',
                    key, old['version'], item['version'], kind.namespace)
                pipeline.unwatch()
                break
            else:
                pipeline.multi()
                pipeline.hset(base_key, key, item_json)
                try:
                    pipeline.execute()
                    # Unlike Redis implementations for other platforms, in redis-py a failed WATCH
                    # produces an exception rather than a null result from execute().
                except redis.exceptions.WatchError:
                    log.debug("RedisFeatureStore: concurrent modification detected, retrying")
                    continue
            self._cache[self._cache_key(kind, key)] = item
            break
예제 #3
0
    def upsert_internal(self, kind, new_item):
        key = self._item_key(kind, new_item['key'])
        encoded_item = json.dumps(new_item)

        # We will potentially keep retrying indefinitely until someone's write succeeds
        while True:
            index, old_value = self._client.kv.get(key)
            if old_value is None:
                mod_index = 0
            else:
                old_item = json.loads(old_value['Value'].decode('utf-8'))
                # Check whether the item is stale. If so, don't do the update (and return the existing item to
                # CachingStoreWrapper so it can be cached)
                if old_item['version'] >= new_item['version']:
                    return old_item
                mod_index = old_value['ModifyIndex']

            # Otherwise, try to write. We will do a compare-and-set operation, so the write will only succeed if
            # the key's ModifyIndex is still equal to the previous value. If the previous ModifyIndex was zero,
            # it means the key did not previously exist and the write will only succeed if it still doesn't exist.
            success = self._client.kv.put(key, encoded_item, cas=mod_index)
            if success:
                return new_item

            log.debug('Concurrent modification detected, retrying')
예제 #4
0
    def _get_even_if_deleted(self, kind, key, check_cache=True):
        cacheKey = self._cache_key(kind, key)
        if check_cache:
            item = self._cache.get(cacheKey)
            if item is not None:
                # reset ttl
                self._cache[cacheKey] = item
                return item

        try:
            r = redis.Redis(connection_pool=self._pool)
            item_json = r.hget(self._items_key(kind), key)
        except BaseException as e:
            log.error(
                "RedisFeatureStore: Could not retrieve key %s from '%s' with error: %s",
                key, kind.namespace, e.message)
            return None

        if item_json is None or item_json is "":
            log.debug(
                "RedisFeatureStore: key %s not found in '%s'. Returning None.",
                key, kind.namespace)
            return None

        item = json.loads(item_json.decode('utf-8'))
        self._cache[cacheKey] = item
        return item
예제 #5
0
 def get(self, kind, key, callback=lambda x: x):
     item = self._get_even_if_deleted(kind, key, check_cache=True)
     if item is not None and item.get('deleted', False) is True:
         log.debug(
             "RedisFeatureStore: get returned deleted item %s in '%s'. Returning None.",
             key, kind.namespace)
         return callback(None)
     return callback(item)
예제 #6
0
    def get_internal(self, kind, key):
        r = redis.Redis(connection_pool=self._pool)
        item_json = r.hget(self._items_key(kind), key)

        if item_json is None or item_json == "":
            log.debug(
                "RedisFeatureStore: key %s not found in '%s'. Returning None.",
                key, kind.namespace)
            return None

        return json.loads(item_json.decode('utf-8'))