mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2025-07-09 04:20:17 +02:00
66 lines
1.9 KiB
Python
66 lines
1.9 KiB
Python
"""Counters."""
|
|
|
|
from typing import Optional
|
|
|
|
from redis import Redis
|
|
|
|
from django.core.cache import cache
|
|
|
|
from .cache import get_redis_client
|
|
|
|
|
|
class ItemCounter:
|
|
"""A process safe item counter.
|
|
|
|
Args:
|
|
- name: Unique name for the counter
|
|
- minimum: Counter can not go below the minimum, when set
|
|
- redis: A Redis client. Will use AA's cache client by default
|
|
"""
|
|
|
|
CACHE_KEY_BASE = "allianceauth-item-counter"
|
|
DEFAULT_CACHE_TIMEOUT = 24 * 3600
|
|
|
|
def __init__(
|
|
self, name: str, minimum: int | None = None, redis: Redis | None = None
|
|
) -> None:
|
|
if not name:
|
|
raise ValueError("Must define a name")
|
|
|
|
self._name = str(name)
|
|
self._minimum = minimum
|
|
self._redis = get_redis_client() if not redis else redis
|
|
|
|
@property
|
|
def _cache_key(self) -> str:
|
|
return f"{self.CACHE_KEY_BASE}-{self._name}"
|
|
|
|
def reset(self, init_value: int = 0):
|
|
"""Reset counter to initial value."""
|
|
with self._redis.lock(f"{self.CACHE_KEY_BASE}-reset"):
|
|
if self._minimum is not None and init_value < self._minimum:
|
|
raise ValueError("Can not reset below minimum")
|
|
|
|
cache.set(self._cache_key, init_value, self.DEFAULT_CACHE_TIMEOUT)
|
|
|
|
def incr(self, delta: int = 1):
|
|
"""Increment counter by delta."""
|
|
try:
|
|
cache.incr(self._cache_key, delta)
|
|
except ValueError:
|
|
pass
|
|
|
|
def decr(self, delta: int = 1):
|
|
"""Decrement counter by delta."""
|
|
with self._redis.lock(f"{self.CACHE_KEY_BASE}-decr"):
|
|
if self._minimum is not None and self.value() == self._minimum:
|
|
return
|
|
try:
|
|
cache.decr(self._cache_key, delta)
|
|
except ValueError:
|
|
pass
|
|
|
|
def value(self) -> int | None:
|
|
"""Return current value or None if not yet initialized."""
|
|
return cache.get(self._cache_key)
|