teoat's picture
Upload core/feature_flags/service.py with huggingface_hub
1d6b864 verified
import hashlib
import json
import logging
from datetime import datetime
from typing import Any
from sqlalchemy.orm import Session
from core.cache import cache_service
from core.feature_flags.models import FeatureFlag
logger = logging.getLogger(__name__)
class FeatureFlagService:
def __init__(self):
self.cache_ttl = 300 # 5 minutes
def is_enabled(
self,
db: Session,
flag_name: str,
user_id: str | None = None,
context: dict[str, Any] | None = None,
) -> bool:
"""
Check if feature flag is enabled for user/context.
Synchronous implementation using synchronous Redis cache and DB.
"""
try:
# Check cache first
cache_key = f"feature_flag:{flag_name}:{user_id or 'global'}"
cached = cache_service.get(cache_key)
if cached is not None:
return bool(cached)
# Fetch from database
flag = db.query(FeatureFlag).filter(FeatureFlag.name == flag_name).first()
if not flag:
# Flag doesn't exist - default to False
return False
if not flag.enabled:
return False
# Rollout percentage logic
if flag.rollout_percentage < 100:
# Consistent hashing
hash_input = f"{flag_name}:{user_id or 'anonymous'}"
user_hash = int(hashlib.md5(hash_input.encode()).hexdigest(), 16) % 100
if user_hash >= flag.rollout_percentage:
return False
# Target users logic
if flag.target_users and user_id:
# target_users is stored as JSON list
target_users_list = (
flag.target_users
if isinstance(flag.target_users, list)
else json.loads(flag.target_users)
)
if user_id not in target_users_list:
return False
# Cache result
cache_service.set(cache_key, True, self.cache_ttl)
return True
except Exception as e:
logger.error(f"Feature flag check failed for {flag_name}: {e}")
return False
def emergency_disable(self, db: Session, flag_name: str, reason: str):
"""
Emergency kill switch - disable flag immediately
"""
logger.warning(f"Emergency disabling flag: {flag_name}. Reason: {reason}")
flag = db.query(FeatureFlag).filter(FeatureFlag.name == flag_name).first()
if flag:
flag.enabled = False
flag.disabled_reason = reason
flag.disabled_at = datetime.now()
db.commit()
# Invalidate cache
try:
cache_service.invalidate_pattern(f"feature_flag:{flag_name}:*")
except Exception as e:
logger.error(f"Failed to invalidate cache for flag {flag_name}: {e}")
feature_flag_service = FeatureFlagService()