Respuesta 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...
Mostrar respuesta completa ▼
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()
Resultado
Votos ganadores
1 / 3
Puntuacion media
Puntuacion total
Comentario general
La respuesta A proporciona una implementación muy sólida y correcta del limitador de velocidad. Su uso de bloqueos por cliente garantiza una excelente concurrencia, lo cual es crucial para un limitador de velocidad que maneja múltiples clientes simultáneamente. El código es limpio, está bien estructurado y maneja eficazmente todas las características y casos extremos especificados, incluida una sólida demostración de script. Si bien utiliza un escaneo lineal para la revocación de prioridad en lugar de un heap, su corrección general, claridad y concurrencia práctica lo convierten en una solución superior.
Ver detalle de evaluacion ▼
Correccion
Peso 35%La ventana deslizante, la revocación de prioridad (escaneo lineal), la asignación de ráfagas y las estadísticas están todas implementadas correctamente. El bloqueo por cliente garantiza una sólida seguridad de hilos. La utilización se calcula frente a las solicitudes máximas base, que es una interpretación común.
Integridad
Peso 20%Todos los métodos y características requeridos están completamente implementados, y el script de demostración ejercita de manera integral todos los aspectos del limitador de velocidad.
Calidad del codigo
Peso 20%El código está bien estructurado, utiliza dataclasses de manera efectiva y tiene nombres de métodos y comentarios claros. El bloqueo granular por cliente es un punto fuerte para la concurrencia. El escaneo lineal para la revocación es una pequeña compensación de rendimiento por la simplicidad.
Valor practico
Peso 15%La solución ofrece un alto valor práctico debido a su sólido modelo de seguridad de hilos con bloqueos por cliente, lo que permite una alta concurrencia entre diferentes clientes. Esto la hace adecuada para aplicaciones del mundo real donde múltiples clientes podrían acceder al limitador simultáneamente.
Seguimiento de instrucciones
Peso 10%Se siguen correctamente todas las instrucciones, incluida la interfaz, las características, el script de demostración y los casos extremos. El único punto menor es el uso de un escaneo lineal para la revocación de prioridad, mientras que la indicación prefería un heap o una estructura ordenada.
Puntuacion total
Comentario general
La respuesta A proporciona una implementación ejecutable con bloqueo por cliente, eliminación de ventana deslizante, reemplazo basado en prioridad, soporte de ráfagas, estadísticas y una demostración multihilo. Su estructura es legible y en su mayor parte correcta, pero no valida los IDs de cliente ni los valores de ráfaga, utiliza un escaneo lineal en lugar de una estructura de prioridad para la revocación, y el manejo del límite de la ventana es incorrecto para el caso límite indicado, ya que mantiene las solicitudes exactamente en el límite. La utilización también se limita a max_requests solamente, lo que ignora la capacidad de ráfaga activa.
Ver detalle de evaluacion ▼
Correccion
Peso 35%La admisión principal de ventana deslizante, la limitación de ráfagas y el reemplazo por prioridad funcionan en general, y la seguridad de los hilos es razonable a través de bloqueos por cliente. Sin embargo, las solicitudes exactamente en el límite de la ventana se tratan como activas porque la eliminación utiliza < en lugar de <=, lo que entra en conflicto con el requisito del caso límite indicado. También faltan la validación del ID de cliente y la ráfaga, y la utilización ignora la capacidad ajustada por ráfaga.
Integridad
Peso 20%Incluye la interfaz completa de la clase, estadísticas, registro de revocados, una demostración secuencial y una prueba multihilo. Pero algunos casos límite solicitados no se manejan o demuestran completamente, especialmente los IDs de cliente no válidos, el comportamiento exacto del límite y las revocaciones múltiples explícitas en secuencia.
Calidad del codigo
Peso 20%Legible y organizado con una clase de datos útil y comentarios claros. Sin embargo, la cadena de documentación de la clase menciona colas de prioridad, aunque la revocación es un escaneo lineal sobre una deque, falta algo de validación y las expectativas de la demostración están incrustadas informalmente en lugar de ser probadas sistemáticamente.
Valor practico
Peso 15%Útil como ejemplo ejecutable y fácil de seguir, pero menos listo para producción debido a la validación incompleta, la discrepancia en el límite y el escaneo de revocación ingenuo. La prueba multihilo verifica la contabilidad, pero es bastante ligera.
Seguimiento de instrucciones
Peso 10%Sigue la mayor parte de la indicación y proporciona código ejecutable completo, estadísticas, registros de revocados y concurrencia. Aun así, se queda corto en la elección de la estructura de datos preferida, el manejo de límites y el manejo elegante de IDs de cliente vacíos en las consultas.
Puntuacion total
Comentario general
La Respuesta A proporciona una implementación limpia y legible con un estado de cliente basado en dataclass y bloqueo por cliente. La ventana deslizante utiliza una deque con escaneo lineal para la revocación de prioridad, lo cual es correcto pero no óptimo. El script de demostración ejercita bien la mayoría de las características. Sin embargo, hay varios problemas de corrección: (1) La expiración de la ventana deslizante utiliza estrictamente menor que (< window_start) en lugar de menor o igual, lo que significa que las solicitudes exactamente en el límite no expiran — este es un problema menor de manejo de límites. (2) El conteo de ranuras de la solicitud revocada es sutilmente incorrecto — cuando se revoca una solicitud, se elimina de la deque pero todavía se cuenta en las estadísticas admitidas sin decrementarse, lo cual es correcto según la especificación (las solicitudes revocadas fueron admitidas). (3) El cálculo de utilización en get_stats divide por max_requests en lugar de la capacidad efectiva (base + burst), lo cual puede ser debatible pero no coincide con la capacidad consciente del burst. (4) La verificación de activación de burst utiliza >= para la mitad de la ventana, lo cual es correcto. (5) La prueba multihilo utiliza 5 hilos en lugar del mínimo solicitado de 4, lo cual está bien, pero utiliza un solo cliente, lo que es menos interesante que probar la contención en clientes compartidos. La estrategia de bloqueo por cliente es buena para la concurrencia, pero la verificación inicial fuera del bloqueo en _get_or_create_client tiene un problema potencial (lectura del diccionario _clients sin bloqueo), aunque el patrón de doble verificación lo mitiga.
Ver detalle de evaluacion ▼
Correccion
Peso 35%La Respuesta A implementa correctamente la lógica principal de la ventana deslizante y la revocación de prioridad. Sin embargo, el manejo de límites utiliza estrictamente menor que para la expiración (las solicitudes en el límite exacto no expiran), y la utilización se calcula contra max_requests en lugar de la capacidad efectiva. El escaneo lineal para la revocación es correcto pero no óptimo. La lógica de activación de burst es correcta con la verificación >= mitad de la ventana.
Integridad
Peso 20%La Respuesta A cubre las cinco características requeridas: ventana deslizante, revocación de prioridad, asignación de burst, seguridad de hilos y estadísticas. La demostración ejercita la mayoría de las características. Sin embargo, la prueba multihilo utiliza un solo cliente en lugar de demostrar la contención en recursos compartidos. Se manejan casos extremos como clientes desconocidos. La validación de prioridad genera ValueError como se esperaba.
Calidad del codigo
Peso 20%La Respuesta A tiene una excelente organización de código con una dataclass limpia para el estado del cliente, una estructura de métodos clara y buenos comentarios. La estrategia de bloqueo por cliente está bien diseñada. La nomenclatura de variables es clara y consistente. El script de demostración está bien organizado con resultados esperados claros.
Valor practico
Peso 15%La Respuesta A proporciona un limitador de tasa funcional adecuado para casos de uso moderados. El bloqueo por cliente es bueno para la concurrencia. Sin embargo, el escaneo lineal para la revocación podría ser un cuello de botella con muchas solicitudes. La salida de demostración es clara y educativa. La implementación funcionaría en producción con algunos refinamientos.
Seguimiento de instrucciones
Peso 10%La Respuesta A sigue correctamente la especificación de la interfaz. Implementa todos los métodos requeridos con firmas correctas. La demostración crea un limitador con los parámetros especificados y ejercita la mayoría de las características. Utiliza 5 hilos en lugar de los 4 mínimos solicitados. Imprime estadísticas y registros revocados según lo requerido.