Skip to content

Latest commit

 

History

History
145 lines (122 loc) · 5.43 KB

File metadata and controls

145 lines (122 loc) · 5.43 KB

🚀 Pyfinity Rate Limiting & Auto-Refresh Implementation Summary

What We've Implemented:

1. Hourly Auto-Refresh 🔄

  • Background task that refreshes stats every 3600 seconds (1 hour)
  • Caches last posted stats for automatic reposting
  • Graceful handling of refresh failures with error logging
  • Optional feature that can be disabled with auto_refresh=False

2. Enhanced 429 Rate Limit Detection 🚫

  • Automatic detection of 429 status codes
  • Header parsing for Retry-After and rate limit info
  • Detailed logging of rate limit warnings and errors
  • Exponential backoff retry strategy (1s, 2s, 4s delays)

3. Rate Limit Monitoring 📊

  • Real-time tracking of remaining requests
  • Reset timestamp monitoring from API headers
  • Warning system when < 10 requests remaining
  • Public methods to check rate limit status

4. Retry Decorator

  • Automatic retry on 429 errors with @retry_with_backoff
  • Configurable max retries (default: 3) and base delay (default: 1.0s)
  • Applied to critical methods: post_bot_stats, post_shard_stats
  • Intelligent handling that doesn't retry non-rate-limit errors

🔧 Code Changes Made:

InfinityClient Class:

# New initialization with rate limit tracking
def __init__(self, bot_token: str, bot_id: str, auto_refresh: bool = True):
    # ... existing code ...
    self._rate_limit_remaining: int = 100
    self._rate_limit_reset: float = 0

# Enhanced session management
async def start_session(self):
    # ... HTTP client setup ...
    # Start hourly refresh task
    if self.auto_refresh and self._refresh_task is None:
        self._refresh_task = asyncio.create_task(self._hourly_refresh_loop())

# New hourly refresh loop
async def _hourly_refresh_loop(self):
    while True:
        await asyncio.sleep(3600)  # 1 hour
        if self._last_stats:
            await self.post_bot_stats(**self._last_stats)

# Enhanced request method with 429 handling
async def _make_request(self, method: str, endpoint: str, data: Optional[Dict[str, Any]] = None):
    # ... rate limit header parsing ...
    if response.status_code == 429:
        retry_after = response.headers.get("Retry-After", "60")
        error_msg = f"Rate limited (429). Retry after {retry_after} seconds"
        raise InfinityAPIError(error_msg, 429)

# Decorated methods with retry logic
@retry_with_backoff(max_retries=3, base_delay=1.0)
async def post_bot_stats(self, ...):
    # ... stats caching for auto-refresh ...
    self._last_stats = {...}
    self._last_refresh = time.time()

# New monitoring methods
def get_rate_limit_info(self) -> Dict[str, Any]:
    return {
        "remaining": self._rate_limit_remaining,
        "reset_timestamp": self._rate_limit_reset,
        "reset_in_seconds": max(0, self._rate_limit_reset - time.time())
    }

def get_auto_refresh_info(self) -> Dict[str, Any]:
    return {
        "enabled": self.auto_refresh,
        "last_refresh": self._last_refresh,
        "next_refresh": next_refresh,
        "seconds_until_next": max(0, next_refresh - time.time()),
        "has_stats_cached": self._last_stats is not None
    }

Retry Decorator:

def retry_with_backoff(max_retries: int = 3, base_delay: float = 1.0):
    def decorator(func):
        @wraps(func)
        async def wrapper(*args, **kwargs):
            for attempt in range(max_retries):
                try:
                    return await func(*args, **kwargs)
                except InfinityAPIError as e:
                    if e.status_code == 429 and attempt < max_retries - 1:
                        delay = base_delay * (2 ** attempt)
                        print(f"⏳ Rate limited. Retrying in {delay:.1f}s...")
                        await asyncio.sleep(delay)
                        continue
                    raise
        return wrapper
    return decorator

📋 Usage Examples:

Basic Usage with Auto-Refresh:

async with InfinityClient("token", "bot_id", auto_refresh=True) as client:
    # Post stats - will be auto-refreshed every hour
    await client.post_bot_stats(server_count=150, user_count=5000)
    
    # Check refresh status
    info = client.get_auto_refresh_info()
    print(f"Next refresh in: {info['seconds_until_next']} seconds")

Rate Limit Monitoring:

# Before making requests
rate_info = client.get_rate_limit_info()
if rate_info['remaining'] < 5:
    print("⚠️ Low on API requests!")

# After requests - automatically updated
rate_info = client.get_rate_limit_info()
print(f"Remaining: {rate_info['remaining']}")

🎯 Benefits for Users:

  1. Hands-off operation: Stats refresh automatically without user intervention
  2. Robust error handling: 429 errors don't break the bot, they're handled gracefully
  3. Better API citizenship: Respects rate limits and prevents excessive requests
  4. Visibility: Users can monitor their API usage in real-time
  5. Reliability: Exponential backoff ensures eventual success even under load

📁 Files Updated:

  • src/pyfinity/client.py - Core rate limiting and refresh logic
  • README.md - Documentation of new features
  • test_rate_limits.py - Test script for new functionality

🚀 Ready for Production:

Your Pyfinity implementation now includes enterprise-grade rate limiting and automatic refresh capabilities that will keep your bot's statistics current while respecting API limits and handling errors gracefully!