"""Check if the specific `id` has at least `amount` tokens, and decrease the bucket if it is the case

        :param id: an id identifying the entity to test, must be convertible to a string
        :param amount: an integer or float value
        :return: whether the entity has the necessary tokens
        """

        key_prefix = f"TokenBucket:{self.name}:{str(id)}:"

        res = token_bucket_script(
            keys=[f"{key_prefix}v", f"{key_prefix}t"],
            args=[self.max, self.interval,
                  get_current_timestamp(), amount],
            client=get_redis_client(),
        )

        return bool(res)

    def reset(self, id):
        key_prefix = f"TokenBucket:{self.name}:{str(id)}:"
        get_redis_client().pipeline().delete(f"{key_prefix}v").delete(
            f"{key_prefix}t").execute()


with open(
        os.path.join(os.path.dirname(os.path.abspath(__file__)),
                     "token_bucket.lua"),
        mode="rb",
) as f:
    token_bucket_script = Script(None, f.read())
Esempio n. 2
0
import six
from django.utils import timezone
from pkg_resources import resource_string
from redis.client import Script

from sentry.tsdb.base import BaseTSDB
from sentry.utils.dates import to_timestamp
from sentry.utils.redis import check_cluster_versions, get_cluster_from_options
from sentry.utils.versioning import Version

logger = logging.getLogger(__name__)

SketchParameters = namedtuple('SketchParameters', 'depth width capacity')

CountMinScript = Script(
    None,
    resource_string('sentry', 'scripts/tsdb/cmsketch.lua'),
)


class RedisTSDB(BaseTSDB):
    """
    A time series storage backend for Redis.

    The time series API supports three data types:

        * simple counters
        * distinct counters (number of unique elements seen)
        * frequency tables (a set of items ranked by most frequently observed)

    The backend also supports virtual nodes (``vnodes``) which controls shard
    distribution. This value should be set to the anticipated maximum number of
Esempio n. 3
0
# Trims a timeline to a maximum number of records.
# Returns the number of keys that were deleted.
# KEYS: {TIMELINE}
# ARGV: {LIMIT}
TRUNCATE_TIMELINE_SCRIPT = """\
local keys = redis.call('ZREVRANGE', KEYS[1], ARGV[1], -1)
for i, record in pairs(keys) do
    redis.call('DEL', KEYS[1] .. ':{TIMELINE_RECORD_PATH_COMPONENT}:' .. record)
    redis.call('ZREM', KEYS[1], record)
end
return table.getn(keys)
""".format(TIMELINE_RECORD_PATH_COMPONENT=TIMELINE_RECORD_PATH_COMPONENT)

# XXX: Passing `None` as the first argument is a dirty hack to allow us to use
# this more easily with the cluster
ensure_timeline_scheduled = Script(None, ENSURE_TIMELINE_SCHEDULED_SCRIPT)
truncate_timeline = Script(None, TRUNCATE_TIMELINE_SCRIPT)


class RedisBackend(Backend):
    """
    Implements the digest backend API, backed by Redis.

    Each timeline is modeled as a sorted set, and also maintains a separate key
    that contains the last time the digest was processed (used for scheduling.)

    .. code::

        redis:6379> ZREVRANGEBYSCORE "d:t:mail:p:1" inf -inf WITHSCORES
        1) "433be20b807c4cd49a132de69c0f6c55"
        2) "1444847625"
Esempio n. 4
0
from pkg_resources import resource_string
from redis.client import Script

from sentry.tsdb.base import BaseTSDB
from sentry.utils.dates import to_datetime, to_timestamp
from sentry.utils.redis import check_cluster_versions, get_cluster_from_options
from sentry.utils.versioning import Version
from six.moves import reduce
from sentry.utils.compat import map
from sentry.utils.compat import zip

logger = logging.getLogger(__name__)

SketchParameters = namedtuple("SketchParameters", "depth width capacity")

CountMinScript = Script(None,
                        resource_string("sentry", "scripts/tsdb/cmsketch.lua"))


class SuppressionWrapper(object):
    """\
    Wraps a context manager and prevents any exceptions raised either during
    the managed block or the exiting of the wrapped manager from propagating.

    You probably shouldn't use this.
    """
    def __init__(self, wrapped):
        self.wrapped = wrapped

    def __enter__(self):
        return self.wrapped.__enter__()
Esempio n. 5
0
# ARGV[1] = id  # noqa: E800
# ARGV[2] = semaphore limit # noqa: E800
# ARGV[3] = expiry in seconds # noqa: E800
# KEY[1] = lock key # noqa: E800
# KEY[2] = signal key # noqa: E800
ACQUIRE_LOCK_SCRIPT = Script(
    None,
    b"""
redis.replicate_commands()
local expire_score = tonumber(redis.call("TIME")[1])
local purged_key_count = redis.call("zremrangebyscore", KEYS[1], '-inf', expire_score)

redis.call("del", KEYS[2])
for i=1,purged_key_count do redis.call("lpush", KEYS[2], 1) end
redis.call("pexpire", KEYS[2], 1000)

if redis.call("zcount", KEYS[1], '-inf', 'inf') < tonumber(ARGV[2]) then
    redis.call("zadd", KEYS[1], expire_score + tonumber(ARGV[3]), ARGV[1])
    return 1
else
    return 0
end
""",
)

# ARGV[1] = id # noqa: E800
# ARGV[2] = expiry in seconds # noqa: E800
# KEY[1] = lock key # noqa: E800
EXTEND_LOCK_SCRIPT = Script(
    None,