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.