Reponse A: Google Gemini 2.5 Pro
import time import threading import random from collections import deque from dataclasses import dataclass, field from typing import List, Tuple, Dict, Optional @dataclass class ClientState: """Holds all state for a single client.""" lock: threading.Lock = field(default_factory=threading.Lock, repr=False) requests: deque[Tuple[float, int]] = field(default_factory=deque) burst: int = 0 stats: Dict[str, int] = field(default_factory=lambda: {"admitted": 0, "rejected": 0, "revoked": 0}) revoked_log: List[Tuple[float, int]] = field(default_factory=list) class RateLimiter: """ A thread...
Afficher la reponse complete ▼
import time import threading import random from collections import deque from dataclasses import dataclass, field from typing import List, Tuple, Dict, Optional @dataclass class ClientState: """Holds all state for a single client.""" lock: threading.Lock = field(default_factory=threading.Lock, repr=False) requests: deque[Tuple[float, int]] = field(default_factory=deque) burst: int = 0 stats: Dict[str, int] = field(default_factory=lambda: {"admitted": 0, "rejected": 0, "revoked": 0}) revoked_log: List[Tuple[float, int]] = field(default_factory=list) class RateLimiter: """ A thread-safe rate limiter implementing a sliding window algorithm with priority queues and burst allowances. """ def __init__(self, max_requests: int, window_seconds: float, default_burst: int = 0): if max_requests <= 0 or window_seconds <= 0: raise ValueError("max_requests and window_seconds must be positive.") self.max_requests = max_requests self.window_seconds = window_seconds self.default_burst = default_burst self._clients: Dict[str, ClientState] = {} self._clients_lock = threading.Lock() def _get_or_create_client(self, client_id: str) -> ClientState: """Atomically get or create a client's state object.""" if client_id not in self._clients: with self._clients_lock: # Double-check locking pattern to avoid race conditions on creation if client_id not in self._clients: self._clients[client_id] = ClientState(burst=self.default_burst) return self._clients[client_id] def set_client_burst(self, client_id: str, burst: int) -> None: """Override burst allowance for a specific client.""" client = self._get_or_create_client(client_id) with client.lock: client.burst = burst def allow(self, client_id: str, priority: int = 3, timestamp: Optional[float] = None) -> bool: """ Check if a request is allowed. If timestamp is None, use current time. Returns True if the request is admitted, False if rejected. """ if not 1 <= priority <= 5: raise ValueError("Priority must be an integer between 1 and 5.") now = timestamp if timestamp is not None else time.time() client = self._get_or_create_client(client_id) with client.lock: # 1. Slide the window: remove expired requests window_start = now - self.window_seconds while client.requests and client.requests[0][0] < window_start: client.requests.popleft() # 2. Determine the effective request limit (base + burst) effective_max = self.max_requests if client.burst > 0 and client.requests: first_req_time = client.requests[0][0] if (now - first_req_time) >= (self.window_seconds / 2.0): effective_max += client.burst current_requests_count = len(client.requests) # 3. Decision: Admit, Revoke, or Reject if current_requests_count < effective_max: # Case A: Space is available (either base or burst) client.requests.append((now, priority)) client.stats['admitted'] += 1 return True else: # Case B: Window is full, check for priority revocation # Find the request with the highest priority number (lowest actual priority) worst_request_idx = -1 worst_priority = -1 for i, req in enumerate(client.requests): if req[1] > worst_priority: worst_priority = req[1] worst_request_idx = i # If a suitable candidate for revocation exists... if worst_request_idx != -1 and worst_priority > priority: # Revoke the lowest-priority request revoked_req = client.requests[worst_request_idx] del client.requests[worst_request_idx] client.revoked_log.append(revoked_req) client.stats['revoked'] += 1 # Admit the new, higher-priority request client.requests.append((now, priority)) client.stats['admitted'] += 1 return True else: # Case C: No space and no possible revocation -> Reject client.stats['rejected'] += 1 return False def get_stats(self, client_id: str) -> dict: """ Return a dict with keys: 'admitted', 'rejected', 'revoked', 'utilization' """ if client_id not in self._clients: return {'admitted': 0, 'rejected': 0, 'revoked': 0, 'utilization': 0.0} client = self._clients[client_id] with client.lock: # Slide window for accurate utilization calculation at the current time now = time.time() window_start = now - self.window_seconds while client.requests and client.requests[0][0] < window_start: client.requests.popleft() current_count = len(client.requests) utilization = min(1.0, current_count / self.max_requests) if self.max_requests > 0 else 0.0 stats_copy = client.stats.copy() stats_copy['utilization'] = utilization return stats_copy def get_revoked_log(self, client_id: str) -> list: """ Return a list of (timestamp, priority) tuples for revoked requests for the given client, in chronological order. """ if client_id not in self._clients: return [] client = self._clients[client_id] with client.lock: # The log is already in chronological order of revocation return list(client.revoked_log) # --- Demonstration Script --- def run_demonstration(): print("--- Rate Limiter Demonstration ---") limiter = RateLimiter(max_requests=5, window_seconds=10.0, default_burst=2) # --- Client 1: Standard, Rejection, and Priority Revocation --- client1 = "client-A" print(f"\n--- Testing Client: {client1} ---") # 1. Fill the window with medium-priority requests print("1. Filling the window (5 requests)...") for i in range(5): limiter.allow(client1, priority=4, timestamp=float(i)) # 2. Reject a request print("2. Sending a 6th request (should be rejected)...") is_allowed = limiter.allow(client1, priority=4, timestamp=5.0) print(f" Request at T=5.0 allowed: {is_allowed} (Expected: False)") # 3. Revoke a low-priority request with a high-priority one print("3. Sending a high-priority request (should revoke one)...") is_allowed = limiter.allow(client1, priority=1, timestamp=6.0) print(f" High-priority request at T=6.0 allowed: {is_allowed} (Expected: True)") # 4. Let the window slide print("4. Waiting for window to slide...") # At T=11.0, request at T=0.0 has expired. Window contains T=1,2,3,4,6 (5 requests) is_allowed = limiter.allow(client1, priority=5, timestamp=11.0) print(f" Request at T=11.0 allowed: {is_allowed} (Expected: False)") # At T=12.0, requests at T=0,1 have expired. Window contains T=2,3,4,6 (4 requests) is_allowed = limiter.allow(client1, priority=5, timestamp=12.0) print(f" Request at T=12.0 allowed: {is_allowed} (Expected: True)") # --- Client 2: Burst Allowance --- client2 = "client-B" print(f"\n--- Testing Client: {client2} (Burst) ---") limiter.set_client_burst(client2, 3) # Override burst to 3 for this client # 1. Send initial request print("1. Sending initial request at T=20.0...") limiter.allow(client2, priority=3, timestamp=20.0) # 2. Try to burst before half-window (should fail) print("2. Attempting to burst before half-window (T=24.0)...") # Window is 10s, half is 5s. T=24.0 is 4s after T=20.0. # Should allow up to max_requests (5) for i in range(4): # Requests 2, 3, 4, 5 limiter.allow(client2, priority=3, timestamp=24.0) is_allowed = limiter.allow(client2, priority=3, timestamp=24.1) print(f" 6th request at T=24.1 (before burst active) allowed: {is_allowed} (Expected: False)") # 3. Activate burst after half-window print("3. Activating burst after half-window (T=25.0)...") # T=25.0 is 5s after T=20.0. Burst is now active. # Limit is now 5 (base) + 3 (burst) = 8. # Currently 5 requests in window (from T=20, T=24). Should allow 3 more. allowed_count = 0 for _ in range(4): # Try to send 4 more if limiter.allow(client2, priority=3, timestamp=25.0): allowed_count += 1 print(f" Allowed {allowed_count} burst requests at T=25.0. (Expected: 3)") # --- Final Stats --- print("\n--- Final Stats ---") print(f"Client A Stats: {limiter.get_stats(client1)}") print(f"Client A Revoked Log: {limiter.get_revoked_log(client1)}") print(f"Client B Stats: {limiter.get_stats(client2)}") print(f"Client B Revoked Log: {limiter.get_revoked_log(client2)}") print(f"Unknown Client Stats: {limiter.get_stats('client-C')}") # --- Multithreaded Test --- def run_multithreaded_test(): print("\n--- Multithreaded Test ---") limiter = RateLimiter(max_requests=20, window_seconds=5.0) client_id = "mt-client" num_threads = 5 requests_per_thread = 10 total_requests = num_threads * requests_per_thread barrier = threading.Barrier(num_threads) def worker(): barrier.wait() for i in range(requests_per_thread): priority = random.randint(1, 5) limiter.allow(client_id, priority=priority) time.sleep(0.01) threads = [threading.Thread(target=worker) for _ in range(num_threads)] for t in threads: t.start() for t in threads: t.join() print(f"\nFinished multithreaded test with {num_threads} threads and {requests_per_thread} requests each.") stats = limiter.get_stats(client_id) print(f"Final Stats for {client_id}: {stats}") total_processed = stats['admitted'] + stats['rejected'] print(f"Total requests attempted: {total_requests}") print(f"Total requests processed (admitted + rejected): {total_processed}") if total_processed == total_requests: print("Test PASSED: All requests were accounted for, indicating thread safety.") else: print(f"Test FAILED: Mismatch in request counts. Expected {total_requests}, got {total_processed}.") if __name__ == '__main__': run_demonstration() run_multithreaded_test()
Resultat
Votes gagnants
1 / 3
Score moyen
Score total
Commentaire global
La réponse A fournit une implémentation très solide et correcte du limiteur de débit. Son utilisation de verrous par client garantit une excellente concurrence, ce qui est crucial pour un limiteur de débit gérant plusieurs clients simultanément. Le code est propre, bien structuré et gère efficacement toutes les fonctionnalités et cas limites spécifiés, y compris un script de démonstration robuste. Bien qu'il utilise un balayage linéaire pour la révocation des priorités au lieu d'un tas, sa correction globale, sa clarté et sa concurrence pratique en font une solution supérieure.
Afficher le detail de l evaluation ▼
Exactitude
Poids 35%La fenêtre glissante, la révocation des priorités (balayage linéaire), la capacité de pointe et les statistiques sont toutes correctement implémentées. Le verrouillage par client garantit une robustesse de la sécurité des threads. L'utilisation est calculée par rapport aux requêtes maximales de base, ce qui est une interprétation courante.
Completude
Poids 20%Toutes les méthodes et fonctionnalités requises sont entièrement implémentées, et le script de démonstration exerce de manière exhaustive tous les aspects du limiteur de débit.
Qualite du code
Poids 20%Le code est bien structuré, utilise efficacement les dataclasses et possède des noms de méthodes et des commentaires clairs. Le verrouillage granulaire par client est un point fort pour la concurrence. Le balayage linéaire pour la révocation est un compromis mineur en termes de performances pour la simplicité.
Valeur pratique
Poids 15%La solution offre une grande valeur pratique en raison de son modèle de sécurité des threads robuste avec des verrous par client, permettant une forte concurrence entre différents clients. Cela la rend adaptée aux applications réelles où plusieurs clients peuvent frapper le limiteur simultanément.
Respect des consignes
Poids 10%Toutes les instructions, y compris l'interface, les fonctionnalités, le script de démonstration et les cas limites, sont correctement suivies. Le seul point mineur est l'utilisation d'un balayage linéaire pour la révocation des priorités, alors que l'invite préférait un tas ou une structure triée.
Score total
Commentaire global
La réponse A fournit une implémentation exécutable avec verrouillage par client, éviction par fenêtre glissante, remplacement basé sur la priorité, prise en charge des rafales, statistiques et une démo multithreadée. Sa structure est lisible et globalement correcte, mais elle ne valide pas les identifiants clients ni les valeurs de rafale, utilise un balayage linéaire plutôt qu'une structure de priorité pour la révocation, et la gestion des limites de fenêtre est incorrecte pour le cas limite déclaré car elle conserve les requêtes exactement à la limite. L'utilisation est également plafonnée uniquement par max_requests, ignorant la capacité de rafale active.
Afficher le detail de l evaluation ▼
Exactitude
Poids 35%L'admission principale par fenêtre glissante, le filtrage des rafales et le remplacement par priorité fonctionnent généralement, et la sécurité des threads est raisonnable grâce aux verrous par client. Cependant, les requêtes exactement à la limite de la fenêtre sont traitées comme toujours actives car l'éviction utilise < au lieu de <=, ce qui contredit l'exigence du cas limite déclaré. La validation des identifiants clients et des rafales est également manquante, et l'utilisation ignore la capacité ajustée par la rafale.
Completude
Poids 20%Inclut l'interface complète de la classe, les statistiques, le journal des éléments révoqués, une démonstration séquentielle et un test multithreadé. Mais certains cas limites demandés ne sont pas entièrement gérés ou démontrés, notamment les identifiants clients invalides, le comportement exact aux limites, et les révocations multiples explicites en séquence.
Qualite du code
Poids 20%Lisible et organisé avec une dataclass utile et des commentaires clairs. Cependant, la docstring de la classe mentionne des files de priorité alors que la révocation est un balayage linéaire sur une deque, certaines validations sont absentes, et les attentes de la démo sont intégrées de manière informelle plutôt que testées systématiquement.
Valeur pratique
Poids 15%Utile comme exemple exécutable et facile à suivre, mais moins prêt pour la production en raison de la validation incomplète, de l'inadéquation des limites et du balayage naïf de la révocation. Le test multithreadé vérifie la comptabilité mais est assez léger.
Respect des consignes
Poids 10%Suit la majeure partie de la consigne et fournit un code exécutable complet, des statistiques, des journaux de révocation et la gestion de la concurrence. Néanmoins, elle n'atteint pas les objectifs en ce qui concerne le choix de la structure de données préférée, la gestion des limites et la gestion gracieuse des identifiants clients vides dans les requêtes.
Score total
Commentaire global
La réponse A fournit une implémentation propre et lisible avec un état client basé sur des dataclasses et un verrouillage par client. La fenêtre glissante utilise une deque avec un balayage linéaire pour la révocation des priorités, ce qui est correct mais pas optimal. Le script de démonstration couvre la plupart des fonctionnalités. Cependant, il y a plusieurs problèmes de correction : (1) L'expiration de la fenêtre glissante utilise un strictement inférieur (< window_start) plutôt qu'un inférieur ou égal, ce qui signifie que les requêtes exactement à la limite ne sont pas expirées — il s'agit d'un problème mineur de gestion des limites. (2) Le comptage des slots de la requête révoquée est subtilement incorrect — lorsqu'une requête est révoquée, elle est supprimée de la deque mais toujours comptée dans les statistiques admises sans être décrémentée, ce qui est correct selon la spécification (les requêtes révoquées ont été admises). (3) Le calcul de l'utilisation dans get_stats divise par max_requests plutôt que par la capacité effective (base + burst), ce qui peut être discutable mais ne correspond pas à la capacité tenant compte du burst. (4) La vérification de l'activation du burst utilise >= pour la moitié de la fenêtre, ce qui est correct. (5) Le test multithread utilise 5 threads au lieu du minimum demandé de 4, ce qui est acceptable, mais utilise un seul client, ce qui est moins intéressant que de tester la contention sur des clients partagés. La stratégie de verrouillage par client est bonne pour la concurrence, mais la vérification initiale en dehors du verrouillage dans _get_or_create_client présente un problème potentiel (lecture du dictionnaire _clients sans verrouillage), bien que le modèle de double vérification atténue cela.
Afficher le detail de l evaluation ▼
Exactitude
Poids 35%La réponse A implémente correctement la logique principale de la fenêtre glissante et de la révocation des priorités. Cependant, la gestion des limites utilise un strictement inférieur pour l'expiration (les requêtes à la limite exacte ne sont pas expirées) et l'utilisation est calculée par rapport à max_requests plutôt qu'à la capacité effective. Le balayage linéaire pour la révocation est correct mais pas optimal. La logique d'activation du burst est correcte avec la vérification >= moitié de la fenêtre.
Completude
Poids 20%La réponse A couvre les cinq fonctionnalités requises : fenêtre glissante, révocation des priorités, allowance de burst, sécurité des threads et statistiques. La démo couvre la plupart des fonctionnalités. Cependant, le test multithread utilise un seul client plutôt que de démontrer la contention sur des ressources partagées. Les cas limites comme les clients inconnus sont gérés. La validation des priorités lève une ValueError comme prévu.
Qualite du code
Poids 20%La réponse A présente une excellente organisation du code avec une dataclass propre pour l'état client, une structure de méthode claire et de bons commentaires. La stratégie de verrouillage par client est bien conçue. La dénomination des variables est claire et cohérente. Le script de démonstration est bien organisé avec des sorties attendues claires.
Valeur pratique
Poids 15%La réponse A fournit un limiteur de débit fonctionnel adapté aux cas d'utilisation modérés. Le verrouillage par client est bon pour la concurrence. Cependant, le balayage linéaire pour la révocation pourrait être un goulot d'étranglement avec de nombreuses requêtes. La sortie de démonstration est claire et éducative. L'implémentation fonctionnerait en production avec quelques améliorations.
Respect des consignes
Poids 10%La réponse A suit correctement la spécification de l'interface. Elle implémente toutes les méthodes requises avec les signatures correctes. La démo crée un limiteur avec les paramètres spécifiés et couvre la plupart des fonctionnalités. Utilise 5 threads au lieu du minimum de 4 demandé. Imprime les statistiques et les logs révoqués comme requis.