Antwort 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...
Vollstandige Antwort anzeigen ▼
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()
Ergebnis
Siegstimmen
1 / 3
Durchschnittsscore
Gesamtpunktzahl
Gesamtkommentar
Antwort A bietet eine sehr starke und korrekte Implementierung des Ratenbegrenzers. Die Verwendung von client-spezifischen Sperren gewährleistet eine ausgezeichnete Nebenläufigkeit, was für einen Ratenbegrenzer, der mehrere Clients gleichzeitig bearbeitet, entscheidend ist. Der Code ist sauber, gut strukturiert und behandelt alle spezifizierten Funktionen und Randfälle effektiv, einschließlich eines robusten Demonstrationsskripts. Obwohl er einen linearen Scan zur Prioritätswiderrufung anstelle eines Heaps verwendet, machen seine allgemeine Korrektheit, Klarheit und praktische Nebenläufigkeit ihn zu einer überlegenen Lösung.
Bewertungsdetails anzeigen ▼
Korrektheit
Gewichtung 35%Das gleitende Fenster, die Prioritätswiderrufung (linearer Scan), die Burst-Zulassung und die Statistiken sind alle korrekt implementiert. Die client-spezifische Sperrung gewährleistet eine robuste Thread-Sicherheit. Die Auslastung wird gegen die Basis-max_requests berechnet, was eine gängige Interpretation ist.
Vollstandigkeit
Gewichtung 20%Alle erforderlichen Methoden und Funktionen sind vollständig implementiert, und das Demonstrationsskript testet umfassend alle Aspekte des Ratenbegrenzers.
Codequalitat
Gewichtung 20%Der Code ist gut strukturiert, verwendet Dataclasses effektiv und hat klare Methodennamen und Kommentare. Die granulare client-spezifische Sperrung ist ein starker Punkt für die Nebenläufigkeit. Der lineare Scan zur Widerrufung ist ein geringer Leistungskompromiss für die Einfachheit.
Praktischer Nutzen
Gewichtung 15%Die Lösung bietet einen hohen praktischen Wert aufgrund ihres robusten Thread-Sicherheitsmodells mit client-spezifischen Sperren, das eine hohe Nebenläufigkeit über verschiedene Clients hinweg ermöglicht. Dies macht sie für reale Anwendungen geeignet, bei denen mehrere Clients gleichzeitig auf den Begrenzer zugreifen könnten.
Befolgung der Anweisungen
Gewichtung 10%Alle Anweisungen, einschließlich der Schnittstelle, der Funktionen, des Demonstrationsskripts und der Randfälle, werden korrekt befolgt. Der einzige geringfügige Punkt ist die Verwendung eines linearen Scans zur Prioritätswiderrufung, während die Aufforderung eine Heap- oder sortierte Struktur bevorzugte.
Gesamtpunktzahl
Gesamtkommentar
Antwort A bietet eine lauffähige Implementierung mit pro-Client-Sperrung, Sliding-Window-Eviction, prioritätsbasierter Ersetzung, Burst-Unterstützung, Statistiken und einer Multithread-Demo. Ihre Struktur ist lesbar und größtenteils korrekt, aber sie validiert keine Client-IDs oder Burst-Werte, verwendet einen linearen Scan anstelle einer Prioritätsstruktur für die Rücknahme und ihre Fenstergrenzenbehandlung ist für den angegebenen Edge-Case falsch, da sie Anfragen genau am Grenzwert beibehält. Die Auslastung wird auch nur gegen max_requests begrenzt, was die aktive Burst-Kapazität ignoriert.
Bewertungsdetails anzeigen ▼
Korrektheit
Gewichtung 35%Die Kern-Sliding-Window-Zulassung, Burst-Begrenzung und Prioritätsersetzung funktionieren im Allgemeinen, und die Thread-Sicherheit ist über pro-Client-Sperren vernünftig. Anfragen genau an der Fenstergrenze werden jedoch als noch aktiv behandelt, da die Eviction < anstelle von <= verwendet, was mit der angegebenen Randfallanforderung kollidiert. Es fehlen auch die Validierung von Client-IDs und Bursts, und die Auslastung ignoriert die Burst-angepasste Kapazität.
Vollstandigkeit
Gewichtung 20%Enthält die vollständige Klassenschnittstelle, Statistiken, ein Protokoll der zurückgenommenen Elemente, eine sequentielle Demonstration und einen Multithread-Test. Einige angeforderte Randfälle werden jedoch nicht vollständig behandelt oder demonstriert, insbesondere ungültige Client-IDs, das exakte Verhalten an den Grenzen und explizite mehrfache Rücknahmen in Folge.
Codequalitat
Gewichtung 20%Lesbar und organisiert mit einer hilfreichen Datenklasse und klaren Kommentaren. Allerdings besagt die Klassen-Docstring, dass Prioritätswarteschlangen verwendet werden, obwohl die Rücknahme ein linearer Scan über eine Deque ist, einige Validierungen fehlen und die Erwartungen der Demo sind informell eingebettet und nicht systematisch getestet.
Praktischer Nutzen
Gewichtung 15%Nützlich als lauffähiges Beispiel und leicht verständlich, aber weniger produktionsreif aufgrund unvollständiger Validierung, Nichtübereinstimmung der Grenzen und naiver Rücknahmescans. Der Multithread-Test prüft die Buchhaltung, ist aber ziemlich leichtgewichtig.
Befolgung der Anweisungen
Gewichtung 10%Folgt den meisten Anweisungen und liefert vollständigen lauffähigen Code, Statistiken, Protokolle der zurückgenommenen Elemente und Nebenläufigkeit. Dennoch bleibt sie bei der bevorzugten Wahl der Datenstruktur, der Behandlung von Grenzen und der ordnungsgemäßen Behandlung leerer Client-IDs in Abfragen zurück.
Gesamtpunktzahl
Gesamtkommentar
Antwort A bietet eine saubere, lesbare Implementierung mit einem datenklassenbasierten Client-Zustand und pro-Client-Sperren. Das gleitende Fenster verwendet eine Deque mit linearem Scan zur Prioritätswiderrufung, was korrekt, aber nicht optimal ist. Das Demonstrationsskript nutzt die meisten Funktionen gut aus. Es gibt jedoch mehrere Korrektheitsprobleme: (1) Die Aussonderung des gleitenden Fensters verwendet strikt kleiner als (< window_start) anstelle von kleiner oder gleich, was bedeutet, dass Anfragen genau an der Grenze nicht abgelaufen sind – dies ist ein geringfügiges Problem bei der Grenzbehandlung. (2) Die Zählung der Slots für widerrufene Anfragen ist subtil falsch – wenn eine Anfrage widerrufen wird, wird sie aus der Deque entfernt, aber immer noch in den zugelassenen Statistiken gezählt, ohne dekrementiert zu werden, was gemäß der Spezifikation korrekt ist (widerrufene Anfragen wurden zugelassen). (3) Die Auslastungsberechnung in get_stats dividiert durch max_requests anstelle der effektiven Kapazität (Basis + Burst), was diskutabel sein mag, aber nicht der Burst-fähigen Kapazität entspricht. (4) Die Prüfung der Burst-Aktivierung verwendet >= für die halbe Fenstergröße, was korrekt ist. (5) Der Multithread-Test verwendet 5 Threads anstelle der angeforderten Mindestanzahl von 4, was in Ordnung ist, aber einen einzelnen Client verwendet, was weniger interessant ist als die Prüfung von Konflikten bei gemeinsam genutzten Clients. Die Pro-Client-Sperrstrategie ist gut für die Nebenläufigkeit, aber die anfängliche Prüfung außerhalb der Sperre in _get_or_create_client hat ein potenzielles Problem (Lesen des _clients-Wörterbuchs ohne Sperre), obwohl das Double-Check-Muster dies abmildert.
Bewertungsdetails anzeigen ▼
Korrektheit
Gewichtung 35%Antwort A implementiert die Kernlogik des gleitenden Fensters und des Prioritätswiderrufs korrekt. Die Grenzbehandlung verwendet jedoch strikt kleiner als für den Ablauf (Anfragen an der exakten Grenze laufen nicht ab) und die Auslastung wird gegen max_requests anstelle der effektiven Kapazität berechnet. Der lineare Scan für den Widerruf ist korrekt, aber nicht optimal. Die Burst-Aktivierungslogik ist mit der Prüfung >= halbe Fenstergröße korrekt.
Vollstandigkeit
Gewichtung 20%Antwort A deckt alle fünf erforderlichen Funktionen ab: gleitendes Fenster, Prioritätswiderruf, Burst-Zulassung, Threadsicherheit und Statistiken. Die Demo nutzt die meisten Funktionen. Der Multithread-Test verwendet jedoch einen einzelnen Client anstelle der Demonstration von Konflikten bei gemeinsam genutzten Ressourcen. Randfälle wie unbekannte Clients werden behandelt. Die Prioritätsvalidierung löst wie erwartet ValueError aus.
Codequalitat
Gewichtung 20%Antwort A hat eine ausgezeichnete Codeorganisation mit einer sauberen Datenklasse für den Client-Zustand, klarer Methodenstruktur und guten Kommentaren. Die Pro-Client-Sperrstrategie ist gut durchdacht. Die Benennung von Variablen ist klar und konsistent. Das Demonstrationsskript ist gut organisiert mit klaren erwarteten Ausgaben.
Praktischer Nutzen
Gewichtung 15%Antwort A bietet einen funktionierenden Ratenbegrenzer, der für moderate Anwendungsfälle geeignet ist. Die Pro-Client-Sperrung ist gut für die Nebenläufigkeit. Der lineare Scan für den Widerruf könnte jedoch bei vielen Anfragen zu einem Engpass werden. Die Demonstrationsausgabe ist klar und lehrreich. Die Implementierung würde mit einigen Verfeinerungen in der Produktion funktionieren.
Befolgung der Anweisungen
Gewichtung 10%Antwort A folgt der Schnittstellenspezifikation korrekt. Sie implementiert alle erforderlichen Methoden mit korrekten Signaturen. Die Demo erstellt einen Limiter mit den angegebenen Parametern und nutzt die meisten Funktionen. Verwendet 5 Threads anstelle der angeforderten Mindestanzahl von 4. Gibt Statistiken und widerrufene Protokolle wie erforderlich aus.