Resposta 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 resposta 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 de vitoria
1 / 3
Pontuacao media
Pontuacao total
Comentario geral
A Resposta A fornece uma implementação muito forte e correta do limitador de taxa. Seu uso de bloqueios por cliente garante excelente concorrência, o que é crucial para um limitador de taxa que lida com vários clientes simultaneamente. O código é limpo, bem estruturado e lida efetivamente com todos os recursos e casos extremos especificados, incluindo um script de demonstração robusto. Embora use uma varredura linear para revogação de prioridade em vez de um heap, sua correção geral, clareza e concorrência prática o tornam uma solução superior.
Ver detalhes da avaliacao ▼
Correcao
Peso 35%A janela deslizante, a revogação de prioridade (varredura linear), a concessão de rajada e as estatísticas são todas implementadas corretamente. O bloqueio por cliente garante robustez contra falhas de thread. A utilização é calculada em relação ao max_requests base, que é uma interpretação comum.
Completude
Peso 20%Todos os métodos e recursos necessários são totalmente implementados, e o script de demonstração exercita completamente todos os aspectos do limitador de taxa.
Qualidade do codigo
Peso 20%O código é bem estruturado, usa dataclasses de forma eficaz e tem nomes de métodos e comentários claros. O bloqueio granular por cliente é um ponto forte para concorrência. A varredura linear para revogação é uma pequena troca de desempenho pela simplicidade.
Valor pratico
Peso 15%A solução oferece alto valor prático devido ao seu modelo robusto de segurança de thread com bloqueios por cliente, permitindo alta concorrência entre diferentes clientes. Isso a torna adequada para aplicações do mundo real onde vários clientes podem atingir o limitador simultaneamente.
Seguimento de instrucoes
Peso 10%Todas as instruções, incluindo a interface, os recursos, o script de demonstração e os casos extremos, são seguidas corretamente. O único ponto menor é o uso de uma varredura linear para revogação de prioridade, enquanto o prompt preferia um heap ou estrutura ordenada.
Pontuacao total
Comentario geral
A Resposta A fornece uma implementação executável com bloqueio por cliente, expiração de janela deslizante, substituição baseada em prioridade, suporte a rajadas, estatísticas e uma demonstração multithread. Sua estrutura é legível e em sua maioria correta, mas não valida IDs de cliente ou valores de rajada, usa uma varredura linear em vez de uma estrutura de prioridade para revogação, e o tratamento de limites de janela está incorreto para o caso de borda declarado, pois mantém as solicitações exatamente no limite. A utilização também é limitada apenas por max_requests, o que ignora a capacidade ativa de rajada.
Ver detalhes da avaliacao ▼
Correcao
Peso 35%A admissão principal da janela deslizante, o controle de rajadas e a substituição de prioridade geralmente funcionam, e a segurança de thread é razoável por meio de bloqueios por cliente. No entanto, as solicitações exatamente no limite da janela são tratadas como ativas porque a expiração usa < em vez de <=, o que conflita com o requisito declarado de caso de borda. A validação de ID de cliente e rajada também está ausente, e a utilização ignora a capacidade ajustada pela rajada.
Completude
Peso 20%Inclui a interface completa da classe, estatísticas, log de revogados, uma demonstração sequencial e um teste multithread. Mas alguns casos de borda solicitados não são totalmente tratados ou demonstrados, especialmente IDs de cliente inválidos, comportamento exato de limite e múltiplas revogações explícitas em sequência.
Qualidade do codigo
Peso 20%Legível e organizado com uma dataclass útil e comentários claros. No entanto, a docstring da classe menciona filas de prioridade, embora a revogação seja uma varredura linear em um deque, falta alguma validação e as expectativas da demonstração estão incorporadas informalmente em vez de serem testadas sistematicamente.
Valor pratico
Peso 15%Útil como um exemplo executável e fácil de seguir, mas menos pronto para produção devido à validação incompleta, incompatibilidade de limites e varredura de revogação ingênua. O teste multithread verifica a contabilidade, mas é bastante leve.
Seguimento de instrucoes
Peso 10%Segue a maior parte do prompt e fornece código executável completo, estatísticas, logs de revogados e concorrência. Ainda assim, fica aquém na escolha preferida da estrutura de dados, tratamento de limites e tratamento gracioso de IDs de cliente vazios em consultas.
Pontuacao total
Comentario geral
A Resposta A fornece uma implementação limpa e legível com um estado de cliente baseado em dataclass e bloqueio por cliente. A janela deslizante usa uma deque com varredura linear para revogação de prioridade, o que está correto, mas não é otimizado. O script de demonstração exercita bem a maioria dos recursos. No entanto, existem várias questões de correção: (1) A expiração da janela deslizante usa estritamente menor que (< window_start) em vez de menor ou igual, o que significa que as requisições exatamente no limite não expiram — este é um problema menor de tratamento de limites. (2) A contagem de slots da requisição revogada está sutilmente errada — quando uma requisição é revogada, ela é removida da deque, mas ainda é contada nas estatísticas admitidas sem ser decrementada, o que está correto de acordo com a especificação (requisições revogadas foram admitidas). (3) O cálculo de utilização em get_stats divide por max_requests em vez da capacidade efetiva (base + burst), o que pode ser discutível, mas não corresponde à capacidade ciente de burst. (4) A verificação de ativação de burst usa >= para meia janela, o que está correto. (5) O teste multithread usa 5 threads em vez do mínimo de 4 solicitado, o que é aceitável, mas usa um único cliente, o que é menos interessante do que testar contenção em clientes compartilhados. A estratégia de bloqueio por cliente é boa para concorrência, mas a verificação inicial fora do bloqueio em _get_or_create_client tem um problema potencial (leitura do dicionário _clients sem bloqueio), embora o padrão de verificação dupla mitigue isso.
Ver detalhes da avaliacao ▼
Correcao
Peso 35%A Resposta A implementa corretamente a lógica principal da janela deslizante e da revogação de prioridade. No entanto, o tratamento de limites usa estritamente menor que para expiração (requisições no limite exato não expiram), e a utilização é calculada contra max_requests em vez da capacidade efetiva. A varredura linear para revogação está correta, mas não é otimizada. A lógica de ativação de burst está correta com a verificação >= meia janela.
Completude
Peso 20%A Resposta A cobre todos os cinco recursos exigidos: janela deslizante, revogação de prioridade, permissão de burst, segurança de thread e estatísticas. A demonstração exercita a maioria dos recursos. No entanto, o teste multithread usa um único cliente em vez de demonstrar contenção em recursos compartilhados. Casos de borda como clientes desconhecidos são tratados. A validação de prioridade levanta ValueError como esperado.
Qualidade do codigo
Peso 20%A Resposta A tem excelente organização de código com uma dataclass limpa para o estado do cliente, estrutura de métodos clara e bons comentários. A estratégia de bloqueio por cliente é bem projetada. A nomenclatura de variáveis é clara e consistente. O script de demonstração é bem organizado com saídas esperadas claras.
Valor pratico
Peso 15%A Resposta A fornece um limitador de taxa funcional adequado para casos de uso moderados. O bloqueio por cliente é bom para concorrência. No entanto, a varredura linear para revogação pode ser um gargalo com muitas requisições. A saída de demonstração é clara e educativa. A implementação funcionaria em produção com alguns refinamentos.
Seguimento de instrucoes
Peso 10%A Resposta A segue a especificação da interface corretamente. Implementa todos os métodos exigidos com assinaturas corretas. A demonstração cria um limitador com os parâmetros especificados e exercita a maioria dos recursos. Usa 5 threads em vez das 4 mínimas solicitadas. Imprime estatísticas e logs revogados conforme exigido.