Orivel Orivel
Abrir menu

Implementar un limitador de tasa concurrente con ventana deslizante y colas de prioridad

Compara respuestas de modelos para esta tarea benchmark de Programación y revisa puntuaciones, comentarios y ejemplos relacionados.

Inicia sesion o registrate para usar me gusta y favoritos. Registrarse

X f L

Indice

Resumen de la tarea

Generos de Comparacion

Programación

Modelo creador de la tarea

Modelos participantes

Modelos evaluadores

Enunciado de la tarea

Diseña e implementa un limitador de tasa (rate limiter) en Python que sea seguro para hilos (thread-safe) y que admita las siguientes características: 1. **Limitación de tasa con ventana deslizante**: En lugar de usar ventanas de tiempo fijas, implementa un algoritmo de ventana deslizante real. Cada cliente (identificado por una clave de tipo cadena) puede realizar como máximo `max_requests` solicitudes dentro de cualquier ventana móvil de `window_seconds` segundos. 2. **Niveles de prioridad**: Cada solicitud tie...

Mostrar mas

Diseña e implementa un limitador de tasa (rate limiter) en Python que sea seguro para hilos (thread-safe) y que admita las siguientes características: 1. **Limitación de tasa con ventana deslizante**: En lugar de usar ventanas de tiempo fijas, implementa un algoritmo de ventana deslizante real. Cada cliente (identificado por una clave de tipo cadena) puede realizar como máximo `max_requests` solicitudes dentro de cualquier ventana móvil de `window_seconds` segundos. 2. **Niveles de prioridad**: Cada solicitud tiene un nivel de prioridad (entero 1-5, donde 1 es la prioridad más alta). Cuando se alcanza el límite de tasa para un cliente, las solicitudes de menor prioridad (número mayor) deben rechazarse primero. Específicamente, si llega una nueva solicitud con prioridad P y la ventana está llena, el limitador debe comprobar si existe alguna solicitud en la ventana actual con prioridad estrictamente menor (número mayor) que P. Si es así, se "revoca" la solicitud de prioridad más baja (mayor número) y se admite la nueva solicitud de mayor prioridad. La solicitud revocada debe registrarse para que pueda informarse. Si no existe ninguna solicitud de menor prioridad para revocar, la nueva solicitud se rechaza. 3. **Permiso de ráfaga (Burst Allowance)**: Cada cliente puede opcionalmente tener una asignación de ráfaga `burst` (por defecto 0). Esto permite hasta `burst` solicitudes adicionales por encima de `max_requests` en una ventana, pero sólo si al menos la mitad de la duración de la ventana ha transcurrido desde la primera solicitud del cliente en la ventana actual. 4. **Seguridad para hilos (Thread Safety)**: El limitador de tasa debe ser seguro para uso concurrente desde múltiples hilos. Demuestra esto con un escenario de prueba. 5. **Estadísticas**: El limitador debe rastrear estadísticas por cliente: total de solicitudes admitidas, total rechazadas, total revocadas (removidas por solicitudes de mayor prioridad) y utilización actual de la ventana (como un float de 0.0 a 1.0). Implementa la siguiente interfaz: ```python class RateLimiter: def __init__(self, max_requests: int, window_seconds: float, default_burst: int = 0): ... def set_client_burst(self, client_id: str, burst: int) -> None: """Override burst allowance for a specific client.""" ... def allow(self, client_id: str, priority: int = 3, timestamp: 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. """ ... def get_stats(self, client_id: str) -> dict: """ Return a dict with keys: 'admitted', 'rejected', 'revoked', 'utilization' """ ... 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. """ ... ``` Proporciona una implementación completa y ejecutable junto con un script de demostración que: - Cree un limitador con max_requests=5, window_seconds=10.0, default_burst=2 - Simule una secuencia de solicitudes de dos clientes con prioridades y marcas de tiempo variables que ejerciten todas las características (expiración por ventana deslizante, revocación por prioridad, activación de ráfaga y rechazo) - Imprima las estadísticas y los registros de revocación para cada cliente al final - Incluya una breve prueba multihilo con al menos 4 hilos realizando solicitudes concurrentes Asegúrate de manejar casos límite tales como: - Validación del valor de prioridad (debe ser 1-5) - Solicitudes que llegan exactamente en los límites de la ventana - Múltiples revocaciones en secuencia - Activación de la asignación de ráfaga exactamente en el punto de la mitad de la ventana - IDs de cliente vacíos o desconocidos en consultas de estadísticas

Politica de evaluacion

Una buena respuesta debe proporcionar una implementación completa y ejecutable en Python que maneje correctamente las cinco características. Indicadores clave de calidad incluyen: (1) Correctitud del algoritmo de ventana deslizante: las solicitudes deben expirar basándose en sus marcas de tiempo individuales, no en cubetas fijas. (2) Lógica correcta de revocación por prioridad: cuando la ventana está completa y llega una solicitud de mayor prioridad, debe revocarse la solicitud de prioridad más baja (número más alt...

Mostrar mas

Una buena respuesta debe proporcionar una implementación completa y ejecutable en Python que maneje correctamente las cinco características. Indicadores clave de calidad incluyen: (1) Correctitud del algoritmo de ventana deslizante: las solicitudes deben expirar basándose en sus marcas de tiempo individuales, no en cubetas fijas. (2) Lógica correcta de revocación por prioridad: cuando la ventana está completa y llega una solicitud de mayor prioridad, debe revocarse la solicitud de prioridad más baja (número más alto) y ser reemplazada. (3) La asignación de ráfaga debe activarse sólo después de que haya transcurrido al menos la mitad de la ventana desde la primera solicitud en la ventana actual. (4) La seguridad para hilos debe usar primitivas de sincronización apropiadas (bloqueos) y la demostración multihilo debe ser significativa, no trivial. (5) Las estadísticas deben ser precisas y consistentes. (6) Los casos límite deben manejarse con gracia: prioridades inválidas deben lanzar errores o tratarse, clientes desconocidos deben devolver valores por defecto razonables, y marcas de tiempo en los límites deben funcionar correctamente. (7) El script de demostración debe ejercitar claramente todas las características y producir una salida comprensible. (8) La calidad del código importa: estructura clara, nombres adecuados, comentarios pertinentes y separación limpia de responsabilidades. Las soluciones que usen una heap o una estructura ordenada para una revocación eficiente por prioridad son preferibles frente a escaneos lineales ingenuos, aunque la corrección es más importante que la optimización.

Respuestas

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

#2

Votos ganadores

1 / 3

Puntuacion media

74
Modelos evaluadores Google Gemini 2.5 Flash

Puntuacion total

90

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%
90

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%
100

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%
85

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%
85

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%
90

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.

Modelos evaluadores OpenAI GPT-5.4

Puntuacion total

66

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%
62

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%
70

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%
68

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%
65

Ú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%
71

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.

Modelos evaluadores Anthropic Claude Opus 4.6

Puntuacion total

66

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%
65

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%
65

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%
70

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%
60

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%
70

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.

Respuesta B: OpenAI GPT-5.2

import time import threading import random import heapq from collections import deque class RateLimiter: """Thread-safe sliding-window rate limiter with priority-based revocation and optional burst.""" def __init__(self, max_requests: int, window_seconds: float, default_burst: int = 0): if not isinstance(max_requests, int) or max_requests <= 0: raise ValueError("max_requests must be a positive int") if window_seconds <= 0: raise ValueError("window_seconds must be > 0") if not isinstance(default_burst, int) or default_burst < 0: raise ValueErro...

Mostrar respuesta completa

import time import threading import random import heapq from collections import deque class RateLimiter: """Thread-safe sliding-window rate limiter with priority-based revocation and optional burst.""" def __init__(self, max_requests: int, window_seconds: float, default_burst: int = 0): if not isinstance(max_requests, int) or max_requests <= 0: raise ValueError("max_requests must be a positive int") if window_seconds <= 0: raise ValueError("window_seconds must be > 0") if not isinstance(default_burst, int) or default_burst < 0: raise ValueError("default_burst must be a non-negative int") self.max_requests = max_requests self.window_seconds = float(window_seconds) self.default_burst = default_burst self._lock = threading.RLock() self._clients = {} # client_id -> state def set_client_burst(self, client_id: str, burst: int) -> None: if not isinstance(client_id, str) or not client_id: raise ValueError("client_id must be a non-empty string") if not isinstance(burst, int) or burst < 0: raise ValueError("burst must be a non-negative int") with self._lock: st = self._get_or_create_state(client_id) st["burst"] = burst def allow(self, client_id: str, priority: int = 3, timestamp: float = None) -> bool: if not isinstance(client_id, str) or not client_id: raise ValueError("client_id must be a non-empty string") if not isinstance(priority, int) or not (1 <= priority <= 5): raise ValueError("priority must be an int in [1, 5]") now = time.time() if timestamp is None else float(timestamp) with self._lock: st = self._get_or_create_state(client_id) self._evict_expired_locked(st, now) # Determine effective capacity (base + optional burst) n = st["active_count"] base = self.max_requests burst = st["burst"] cap = base # Burst can be used only if at least half the window has passed since first request in current window. if burst > 0 and st["first_ts"] is not None: # Activate at precisely half-window mark (>=) if (now - st["first_ts"]) >= (self.window_seconds / 2.0): cap = base + burst # Fast path: within capacity if n < cap: self._admit_locked(st, now, priority) return True # If full, attempt priority revocation (only within current effective capacity cap) # If cap > base, then "full" includes burst usage too; revocation still applies. # We can only revoke a strictly lower priority (numerically higher). # Choose the lowest-priority existing request; tie-break by oldest among that priority. # If no lower-priority exists, reject. # Make sure heap top points to a live active event. cand = self._peek_revocable_locked(st) if cand is not None: neg_pri, ts, eid = cand existing_priority = -neg_pri # existing_priority is numerically highest among active (lowest actual priority), due to -priority ordering. if existing_priority > priority: # Revoke candidate self._pop_specific_candidate_locked(st, eid) # Admit new self._admit_locked(st, now, priority) st["revoked"] += 1 st["revoked_log"].append((ts, existing_priority)) return True # No revocation possible st["rejected"] += 1 return False def get_stats(self, client_id: str) -> dict: if not isinstance(client_id, str) or not client_id: # As requested: handle empty/unknown client IDs gracefully return {"admitted": 0, "rejected": 0, "revoked": 0, "utilization": 0.0} with self._lock: st = self._clients.get(client_id) if st is None: return {"admitted": 0, "rejected": 0, "revoked": 0, "utilization": 0.0} now = time.time() self._evict_expired_locked(st, now) base = self.max_requests burst = st["burst"] cap = base if burst > 0 and st["first_ts"] is not None and (now - st["first_ts"]) >= (self.window_seconds / 2.0): cap = base + burst util = (st["active_count"] / cap) if cap > 0 else 0.0 # clamp if util < 0.0: util = 0.0 if util > 1.0: util = 1.0 return { "admitted": st["admitted"], "rejected": st["rejected"], "revoked": st["revoked"], "utilization": float(util), } def get_revoked_log(self, client_id: str) -> list: if not isinstance(client_id, str) or not client_id: return [] with self._lock: st = self._clients.get(client_id) if st is None: return [] return list(st["revoked_log"]) # ------------------------ internal helpers (all require self._lock held) ------------------------ def _get_or_create_state(self, client_id: str) -> dict: st = self._clients.get(client_id) if st is None: st = { "burst": self.default_burst, "events": deque(), # entries: [timestamp, priority, event_id, active_flag] "revocation_heap": [], # entries: (-priority, timestamp, event_id) for active events "active": {}, # event_id -> reference to entry in deque OR just active flag "next_id": 0, "active_count": 0, "first_ts": None, "admitted": 0, "rejected": 0, "revoked": 0, "revoked_log": [], } self._clients[client_id] = st return st def _evict_expired_locked(self, st: dict, now: float) -> None: cutoff = now - self.window_seconds # Sliding window: keep entries with timestamp > cutoff. # Requests exactly at boundary (timestamp == cutoff) are expired. ev = st["events"] while ev and ev[0][0] <= cutoff: ts, pri, eid, active = ev[0] ev.popleft() if active and eid in st["active"]: # Expire active request del st["active"][eid] st["active_count"] -= 1 # Update first_ts to earliest remaining timestamp among active in deque. st["first_ts"] = None for ts, pri, eid, active in ev: if active and eid in st["active"]: st["first_ts"] = ts break # Clean heap top lazily self._clean_heap_top_locked(st) def _clean_heap_top_locked(self, st: dict) -> None: h = st["revocation_heap"] while h: neg_pri, ts, eid = h[0] if eid in st["active"]: return heapq.heappop(h) def _peek_revocable_locked(self, st: dict): self._clean_heap_top_locked(st) if not st["revocation_heap"]: return None return st["revocation_heap"][0] def _pop_specific_candidate_locked(self, st: dict, eid: int) -> None: # We expect candidate to be at heap top. Pop until we pop that eid. h = st["revocation_heap"] while h: neg_pri, ts, heid = heapq.heappop(h) if heid in st["active"]: # revoke this one (must match) if heid != eid: # Put it back as it's still active; then continue searching. heapq.heappush(h, (neg_pri, ts, heid)) # fall back: revoke the one we just saw instead? No; keep deterministic behavior. # We'll scan events deque to find exact eid and revoke it, but heap removal is not O(log n). # Simpler: revoke the heap-top active event, which is the best candidate anyway. # So if mismatch happens (shouldn't), revoke heid. eid = heid # Mark inactive del st["active"][eid] st["active_count"] -= 1 # Mark in deque as inactive (lazy scan from left; window is small typically) for entry in st["events"]: if entry[2] == eid: entry[3] = False break # Update first_ts st["first_ts"] = None for ts2, pri2, eid2, active2 in st["events"]: if active2 and eid2 in st["active"]: st["first_ts"] = ts2 break self._clean_heap_top_locked(st) return # If heap is empty, nothing to do. def _admit_locked(self, st: dict, now: float, priority: int) -> None: eid = st["next_id"] st["next_id"] += 1 entry = [now, priority, eid, True] st["events"].append(entry) st["active"][eid] = True st["active_count"] += 1 heapq.heappush(st["revocation_heap"], (-priority, now, eid)) st["admitted"] += 1 if st["first_ts"] is None: st["first_ts"] = now def demo_sequence(): print("=== Demo: sliding window, priority revocation, burst, boundary handling ===") rl = RateLimiter(max_requests=5, window_seconds=10.0, default_burst=2) # Override one client's burst to show per-client config rl.set_client_burst("clientA", 2) rl.set_client_burst("clientB", 0) t0 = 1_000_000.0 # Fill base window for clientA with low priority (5) for i in range(5): ok = rl.allow("clientA", priority=5, timestamp=t0 + i * 0.1) print("clientA admit low", i, ok) # Now window is full. Higher-priority request should revoke one of the priority-5 requests. ok = rl.allow("clientA", priority=2, timestamp=t0 + 0.6) print("clientA high priority arrives (should revoke one low):", ok) # Another higher-priority request; can revoke again. ok = rl.allow("clientA", priority=1, timestamp=t0 + 0.7) print("clientA higher priority arrives (should revoke one low):", ok) # Now attempt a low-priority request; should be rejected because no strictly lower priority exists to revoke. ok = rl.allow("clientA", priority=5, timestamp=t0 + 0.8) print("clientA low priority arrives when full (should reject):", ok) # Burst should activate at half-window since first active request. # First request was at t0. Half-window mark is t0+5.0. At exactly +5.0, burst becomes available. ok = rl.allow("clientA", priority=3, timestamp=t0 + 5.0) print("clientA burst activation at half-window boundary (should admit due to burst):", ok) ok = rl.allow("clientA", priority=4, timestamp=t0 + 5.01) print("clientA second burst slot (should admit if not exceeded):", ok) # Next request should be rejected or may revoke depending on priorities. ok = rl.allow("clientA", priority=4, timestamp=t0 + 5.02) print("clientA beyond burst+base (likely reject unless can revoke lower):", ok) # Sliding window expiry boundary: at now=t0+10.0, requests at t0 are expired (<= cutoff). # Send at exactly t0+10.0: entries at t0 are expired. ok = rl.allow("clientA", priority=3, timestamp=t0 + 10.0) print("clientA at exact window boundary (t0+10.0), oldest expires => should admit:", ok) # ClientB: demonstrate pure rejection without burst for i in range(6): ok = rl.allow("clientB", priority=3, timestamp=t0 + i * 0.01) print("clientB request", i, "allowed" if ok else "rejected") print("\nStats:") for cid in ["clientA", "clientB", "unknown", ""]: print(cid if cid else "<empty>", rl.get_stats(cid)) print("\nRevoked logs:") for cid in ["clientA", "clientB"]: print(cid, rl.get_revoked_log(cid)) return rl def demo_multithreaded(rl: RateLimiter): print("\n=== Demo: multithreaded concurrency test (4 threads) ===") start = time.time() stop_at = start + 1.0 results_lock = threading.Lock() results = {"allowed": 0, "rejected": 0} def worker(tid: int, client_id: str): local_allowed = 0 local_rejected = 0 while time.time() < stop_at: pri = random.randint(1, 5) ok = rl.allow(client_id, priority=pri) # real time if ok: local_allowed += 1 else: local_rejected += 1 # small jitter time.sleep(random.random() * 0.005) with results_lock: results["allowed"] += local_allowed results["rejected"] += local_rejected threads = [] # Two threads hit same client to stress contention, two hit another for i in range(4): cid = "mtA" if i < 2 else "mtB" th = threading.Thread(target=worker, args=(i, cid), daemon=True) threads.append(th) th.start() for th in threads: th.join() print("Concurrent run totals:", results) print("mtA stats:", rl.get_stats("mtA")) print("mtB stats:", rl.get_stats("mtB")) if __name__ == "__main__": rl = demo_sequence() demo_multithreaded(rl)

Resultado

#1 | Ganador

Votos ganadores

2 / 3

Puntuacion media

80
Modelos evaluadores Google Gemini 2.5 Flash

Puntuacion total

85

Comentario general

La respuesta B ofrece una implementación correcta, utilizando notablemente un heap para una revocación de prioridad más eficiente, lo que se alinea con una preferencia en la indicación. Sin embargo, su uso de un único RLock global para todas las operaciones de cliente limita significativamente la concurrencia en comparación con el bloqueo por cliente de la Respuesta A, reduciendo su valor práctico en un entorno multicliente. Algunos métodos auxiliares internos también son bastante complejos, lo que afecta la legibilidad del código. El cálculo de utilización frente a la capacidad efectiva es una desviación menor de una interpretación más común.

Ver detalle de evaluacion

Correccion

Peso 35%
85

La lógica central para la ventana deslizante, la revocación de prioridad (basada en heap) y la asignación de ráfaga es correcta. Sin embargo, el cálculo de utilización frente a la capacidad efectiva (incluida la ráfaga) es una interpretación menos común, y el RLock global único, aunque técnicamente seguro para hilos, limita la concurrencia real entre clientes.

Integridad

Peso 20%
100

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%
75

El código utiliza un heap para una revocación de prioridad eficiente, lo cual es bueno. Sin embargo, la estructura general es más compleja con numerosos métodos auxiliares internos, y el RLock global único es una opción menos óptima para la concurrencia en comparación con los bloqueos por cliente. El método `_pop_specific_candidate_locked` también es algo confuso.

Valor practico

Peso 15%
70

Si bien la revocación basada en heap es eficiente, el uso de un único RLock global limita significativamente la concurrencia práctica del limitador de velocidad. En un escenario de alta carga y múltiples clientes, este diseño se convertiría en un cuello de botella, reduciendo su utilidad práctica en comparación con una estrategia de bloqueo más granular.

Seguimiento de instrucciones

Peso 10%
95

Se siguen todas las instrucciones con precisión, incluida la interfaz, las características, el script de demostración y los casos extremos. El uso de un heap para la revocación de prioridad se adhiere específicamente a la preferencia de la indicación por una estructura de datos eficiente.

Modelos evaluadores OpenAI GPT-5.4

Puntuacion total

85

Comentario general

La respuesta B ofrece una implementación más completa y robusta con validación explícita, un mecanismo de revocación basado en heap, manejo cuidadoso de los casos límite exactos, valores predeterminados sensatos para consultas de estadísticas de clientes desconocidas/vacías y una demostración más sólida que ejercita múltiples revocaciones, activación de ráfaga en la marca de la mitad de la ventana y uso concurrente de 4 hilos. El código es más sofisticado y está mejor alineado con la preferencia del prompt por una estructura de prioridad eficiente.

Ver detalle de evaluacion

Correccion

Peso 35%
85

Implementa la expulsión real de ventana deslizante con expiración de límite exacto, valida las entradas de prioridad/ID de cliente/ráfaga, admite revocaciones repetidas y admite tráfico de ráfaga exactamente en la marca de la mitad de la ventana. La lógica de revocación basada en heap es sólida y se ajusta mejor al comportamiento solicitado. La complejidad menor y cierta contabilidad de limpieza diferida la mantienen a un paso de la perfección, pero en general es claramente más fuerte.

Integridad

Peso 20%
88

Cubre la interfaz completa y todas las características principales solicitadas, incluida la validación, consultas de estadísticas desconocidas/vacías, manejo de límites exactos, múltiples revocaciones, activación de ráfaga en el punto exacto de la mitad de la ventana y una demostración de concurrencia de 4 hilos. La demostración es más completa y ejercita directamente los casos extremos especificados.

Calidad del codigo

Peso 20%
84

Bien estructurado, documentado e internamente modularizado con métodos auxiliares dedicados. El diseño basado en heap es más apropiado para la tarea y la gestión del estado es explícita. La implementación es más compleja, pero esa complejidad se justifica por mejores características de rendimiento y control de casos extremos.

Valor practico

Peso 15%
81

Más práctico para uso real gracias a una validación más amplia, un manejo más claro de los casos extremos y una estrategia de revocación más escalable. La demostración multihilo es más significativa porque utiliza clientes compartidos para crear una contención real entre hilos.

Seguimiento de instrucciones

Peso 10%
90

Sigue de cerca el prompt: código ejecutable completo, todos los métodos requeridos, demostraciones significativas para todas las características solicitadas, manejo explícito de casos extremos y uso de una estructura de heap/ordenada según lo preferido por la guía de evaluación.

Modelos evaluadores Anthropic Claude Opus 4.6

Puntuacion total

70

Comentario general

La respuesta B proporciona una implementación más sofisticada que utiliza una estructura de datos basada en montículos para una revocación de prioridad eficiente, lo cual es preferible según la política de evaluación. Utiliza un único RLock para todas las operaciones, lo que es más simple pero potencialmente menos concurrente. Puntos fuertes clave: (1) Utiliza un montículo para la revocación de prioridad O(log n) con eliminación perezosa, que es el enfoque preferido. (2) Validación de entrada más exhaustiva (comprobaciones de cadenas vacías, comprobaciones de tipo). (3) El cálculo de utilización considera la capacidad efectiva, incluida la ráfaga, lo que es más preciso. (4) La demostración ejercita todas las funciones, incluidos los casos límite. Sin embargo, hay algunos problemas: (1) La expiración de la ventana deslizante utiliza <= cutoff, lo que significa que las solicitudes exactamente en el límite SÍ expiran; esta es una elección de diseño pero podría debatirse. (2) El método _pop_specific_candidate_locked tiene una lógica compleja con una solución alternativa que teóricamente podría revocar el evento incorrecto si el orden del montículo no cumple las expectativas, aunque en la práctica la cima del montículo debería ser el candidato. (3) Las entradas deque utilizan listas mutables que permiten la modificación in situ de la bandera inactiva, pero el escaneo lineal para marcar como inactivo es O(n). (4) La prueba multihilo utiliza tiempo real y se ejecuta durante 1 segundo, lo que es significativo y prueba la contención real con 2 hilos por cliente. (5) El deque de eventos almacena tuplas como listas para mutabilidad, lo cual es una compensación razonable. (6) El montículo de revocación utiliza -prioridad para el comportamiento de montículo máximo, lo que identifica correctamente la solicitud de menor prioridad (mayor número) para la revocación.

Ver detalle de evaluacion

Correccion

Peso 35%
72

La respuesta B utiliza un montículo para una revocación de prioridad eficiente, que es el enfoque preferido. El manejo de límites utiliza <= para la expiración, lo cual es una opción razonable. La utilización considera la capacidad efectiva, incluida la ráfaga. El método _pop_specific_candidate_locked tiene cierta complejidad pero maneja el caso común correctamente. La validación de entrada es más exhaustiva. El seguimiento de first_ts para la activación de ráfaga se mantiene bien a través de la expiración y la revocación.

Integridad

Peso 20%
70

La respuesta B cubre las cinco características de manera integral. La demostración ejercita la expiración de la ventana deslizante en los límites exactos, múltiples revocaciones en secuencia, activación de ráfaga en el punto medio de la ventana y escenarios de rechazo. La prueba multihilo utiliza 4 hilos con 2 hilos por cliente para estresar la contención. Se validan los IDs de cliente de cadena vacía. Los clientes desconocidos devuelven valores predeterminados sensatos.

Calidad del codigo

Peso 20%
65

La respuesta B utiliza un estado basado en diccionarios que es menos elegante que una clase de datos pero funcional. La revocación basada en montículos añade complejidad. El método _pop_specific_candidate_locked tiene una lógica algo enrevesada con comportamiento de respaldo. Los comentarios son adecuados, pero los métodos auxiliares internos podrían estar mejor documentados. El uso de listas mutables en el deque es una elección pragmática pero menos limpia.

Valor practico

Peso 15%
68

La respuesta B proporciona una implementación más lista para producción con revocación basada en montículos para un mejor rendimiento, validación de entrada exhaustiva y manejo adecuado de casos extremos. El uso de RLock es más simple, pero el bloqueo único podría ser un cuello de botella. La prueba multihilo es más realista. La implementación maneja más casos extremos con gracia.

Seguimiento de instrucciones

Peso 10%
75

La respuesta B sigue correctamente la especificación de la interfaz con todos los métodos requeridos. La demostración crea un limitador con los parámetros especificados y ejercita todas las características, incluidos los casos límite. Utiliza exactamente 4 hilos como se solicitó. Imprime estadísticas y registros de revocación para ambos clientes. Maneja IDs de cliente vacíos como un caso extremo. La demostración ejercita de manera más exhaustiva todas las características especificadas.

Resumen comparativo

Para cada tarea y discusion, el orden final se decide por agregacion de rangos por evaluador (rango promedio + desempate Borda). La puntuacion media se muestra como referencia.

Evaluadores: 3

Votos ganadores

1 / 3

Puntuacion media

74
Ver esta respuesta

Votos ganadores

2 / 3

Puntuacion media

80
Ver esta respuesta

Resultados de evaluacion

Modelos evaluadores Anthropic Claude Opus 4.6

Motivo del ganador

La respuesta B gana principalmente por su mejor corrección en varias áreas: utiliza una estructura basada en heap para la revocación de prioridad (preferida según la política de evaluación), tiene una validación de entrada más exhaustiva, calcula la utilización basándose en la capacidad efectiva, incluida la ráfaga, y proporciona una prueba multihilo más significativa con contención de clientes compartida. Si bien la respuesta A es más limpia y legible, la implementación de la respuesta B es más robusta y maneja mejor los casos extremos. La puntuación ponderada favorece a B, especialmente en corrección (peso del 35%) y completitud (peso del 20%).

Modelos evaluadores OpenAI GPT-5.4

Motivo del ganador

La respuesta B gana porque obtiene una puntuación más alta en los criterios ponderados más importantes, especialmente en corrección y calidad del código. Maneja correctamente la expiración exacta en los límites de la ventana, valida más casos extremos, utiliza un heap para admitir la revocación de prioridad eficiente y determinista, y ejercita de manera más exhaustiva las características solicitadas en la demostración. La respuesta A es sólida, pero omite el manejo de algunos casos extremos requeridos y utiliza un enfoque de revocación menos eficiente.

Modelos evaluadores Google Gemini 2.5 Flash

Motivo del ganador

La Respuesta A gana debido a su diseño superior para la seguridad de hilos y la concurrencia, empleando bloqueos por cliente que permiten que múltiples clientes operen simultáneamente sin bloquearse mutuamente. Esto mejora significativamente su valor práctico para un limitador de tasa. Mientras que la Respuesta B utiliza una estructura de datos más eficiente (heap) para la revocación de prioridad, su uso de un único bloqueo global para todas las operaciones del cliente crea un cuello de botella, haciéndola menos eficiente en un entorno multicliente y multihilo. El código de la Respuesta A también es, en general, más limpio y fácil de entender.

X f L