Resposta A: Google Gemini 2.5 Flash-Lite
Resposta:
import time
import threading
import collections
import heapq
import uuid
from dataclasses import dataclass, field
from typing import Dict, List, Tuple, Any, Optional
# --- Configuração ---
# Define os níveis com os respetivos limites de taxa e tamanhos de janela.
# Um window_seconds menor significa limitação de taxa mais restrita.
TIER_CONFIG = {
"free": {"max_requests": 5, "window_seconds": 60},
"standard": {"max_requests": 50, "window_seconds": 60},
"premium": {"max_requests": 200, "window_seconds": 60},
}
# Quanto tempo manter os dados do cliente antes da limpeza se não houv...
Mostrar resposta completa ▼
Resposta:
import time
import threading
import collections
import heapq
import uuid
from dataclasses import dataclass, field
from typing import Dict, List, Tuple, Any, Optional
# --- Configuração ---
# Define os níveis com os respetivos limites de taxa e tamanhos de janela.
# Um window_seconds menor significa limitação de taxa mais restrita.
TIER_CONFIG = {
"free": {"max_requests": 5, "window_seconds": 60},
"standard": {"max_requests": 50, "window_seconds": 60},
"premium": {"max_requests": 200, "window_seconds": 60},
}
# Quanto tempo manter os dados do cliente antes da limpeza se não houver atividade.
CLEANUP_THRESHOLD_SECONDS = 300
# --- Estruturas de Dados ---
@dataclass(order=True)
class Request:
"""Representa um pedido com prioridade, para a fila de prioridade."""
# Usamos priority como o primeiro elemento para o heapq ordenar por ele.
# Número menor significa prioridade mais alta.
priority: int
# Usa um ID único para desempatar prioridades e garantir ordenação estável.
request_id: str = field(default_factory=lambda: str(uuid.uuid4()))
client_id: str
timestamp: float
payload: Any
def __lt__(self, other):
# Comparação personalizada para heapq: prioriza por prioridade, depois por request_id para estabilidade.
if self.priority != other.priority:
return self.priority < other.priority
return self.request_id < other.request_id
class SlidingWindowCounter:
"""Implementa um contador de janela deslizante para limitação de taxa."""
def __init__(self, max_requests: int, window_seconds: float):
self.max_requests = max_requests
self.window_seconds = window_seconds
# Usa um deque para armazenar os carimbos temporais dos pedidos dentro da janela.
# Deques oferecem appends e pops eficientes em ambas as extremidades.
self.request_timestamps: collections.deque[float] = collections.deque()
# Lock para acesso thread-safe a request_timestamps.
self.lock = threading.Lock()
def record_request(self) -> bool:
"""Regista um pedido e devolve True se estiver dentro do limite, False caso contrário."""
with self.lock:
current_time = time.time()
# Remove carimbos temporais mais antigos do que a janela.
while self.request_timestamps and self.request_timestamps[0] <= current_time - self.window_seconds:
self.request_timestamps.popleft()
# Verifica se adicionar um novo pedido excede o limite.
if len(self.request_timestamps) < self.max_requests:
self.request_timestamps.append(current_time)
return True
else:
return False
def get_current_count(self) -> int:
"""Devolve o número atual de pedidos dentro da janela."""
with self.lock:
current_time = time.time()
# Limpa carimbos temporais antigos antes de devolver a contagem.
while self.request_timestamps and self.request_timestamps[0] <= current_time - self.window_seconds:
self.request_timestamps.popleft()
return len(self.request_timestamps)
class RateLimiter:
"""Um limitador de taxa thread-safe com múltiplos níveis, janela deslizante e filas de prioridade."""
def __init__(self, tier_config: Dict[str, Dict[str, Any]], cleanup_threshold_seconds: float):
self.tier_config = tier_config
self.cleanup_threshold_seconds = cleanup_threshold_seconds
# Armazena os contadores de janela deslizante para cada nível.
self.tier_counters: Dict[str, SlidingWindowCounter] = {}
# Armazena a fila de prioridade para pedidos adiados de cada nível.
self.tier_queues: Dict[str, List[Request]] = collections.defaultdict(list)
# Armazena o carimbo temporal da última atividade de cada cliente para limpeza.
self.client_last_activity: Dict[str, float] = {}
# Armazena o nível atribuído a cada cliente.
self.client_tiers: Dict[str, str] = {}
# Lock principal para proteger estruturas de dados partilhadas como client_tiers, client_last_activity e tier_queues.
# O SlidingWindowCounter tem o seu próprio lock interno.
self.global_lock = threading.Lock()
# Inicializa os contadores dos níveis.
for tier_name, config in tier_config.items():
self.tier_counters[tier_name] = SlidingWindowCounter(
max_requests=config["max_requests"],
window_seconds=config["window_seconds"]
)
# Inicia uma thread em segundo plano para limpeza.
self.cleanup_thread = threading.Thread(target=self._cleanup_task, daemon=True)
self.cleanup_thread.start()
def register_client(self, client_id: str, tier: str) -> bool:
"""Regista um cliente com um nível específico. Devolve True se for bem-sucedido, False se o cliente já existir ou se o nível for inválido."""
if tier not in self.tier_config:
print(f"[!] Erro: O nível '{tier}' não existe.")
return False
with self.global_lock:
if client_id in self.client_tiers:
print(f"[!] Aviso: O cliente '{client_id}' já está registado.")
return False
self.client_tiers[client_id] = tier
self.client_last_activity[client_id] = time.time()
print(f"[*] Cliente '{client_id}' registado com o nível '{tier}'.")
return True
def _get_client_tier(self, client_id: str) -> Optional[str]:
"""Obtém com segurança o nível para um determinado client_id."""
with self.global_lock:
return self.client_tiers.get(client_id)
def _update_client_activity(self, client_id: str):
"""Atualiza o carimbo temporal da última atividade de um cliente."""
with self.global_lock:
self.client_last_activity[client_id] = time.time()
def allow_request(self, client_id: str) -> bool:
"""Verifica se um pedido de client_id é permitido com base no limite de taxa do seu nível."""
tier = self._get_client_tier(client_id)
if not tier:
print(f"[!] Erro: O cliente '{client_id}' não está registado.")
return False
counter = self.tier_counters.get(tier)
if not counter:
# Isto não deve acontecer se tier_config for válido e a inicialização estiver correta.
print(f"[!] Erro interno: Contador não encontrado para o nível '{tier}'.")
return False
if counter.record_request():
self._update_client_activity(client_id)
# print(f"[*] Pedido PERMITIDO para o cliente '{client_id}' (Nível: {tier}). Contagem atual: {counter.get_current_count()}")
return True
else:
# print(f"[*] Pedido NEGADO para o cliente '{client_id}' (Nível: {tier}). Contagem atual: {counter.get_current_count()}")
return False
def enqueue_request(self, client_id: str, priority: int, payload: Any):
"""Coloca um pedido limitado por taxa na fila de prioridade apropriada."""
tier = self._get_client_tier(client_id)
if not tier:
print(f"[!] Erro: O cliente '{client_id}' não está registado. Não é possível enfileirar.")
return
request = Request(priority=priority, client_id=client_id, timestamp=time.time(), payload=payload)
with self.global_lock:
heapq.heappush(self.tier_queues[tier], request)
print(f"[*] Pedido ENFILEIRADO para o cliente '{client_id}' (Nível: {tier}, Prioridade: {priority}). Tamanho da fila: {len(self.tier_queues[tier])}")
self._update_client_activity(client_id)
def dequeue_and_process(self, client_id: str) -> Optional[Tuple[Request, bool]]:
"""Tenta retirar da fila e processar o pedido de maior prioridade para o nível de um cliente se houver capacidade disponível."""
tier = self._get_client_tier(client_id)
if not tier:
print(f"[!] Erro: O cliente '{client_id}' não está registado. Não é possível desenfileirar.")
return None
counter = self.tier_counters.get(tier)
if not counter:
print(f"[!] Erro interno: Contador não encontrado para o nível '{tier}'.")
return None
# Verifica se há capacidade disponível ANTES de tentar retirar da fila.
# Isto evita locking e operações de heap desnecessários se não existir capacidade.
if counter.get_current_count() < counter.max_requests:
with self.global_lock:
queue = self.tier_queues[tier]
if queue:
# Revalida a capacidade dentro do lock para lidar com race conditions.
# É possível que outra thread tenha acabado de preencher a capacidade.
if counter.get_current_count() < counter.max_requests:
highest_priority_request = heapq.heappop(queue)
# Tenta registar o pedido novamente. Isto deve resultar se houver capacidade disponível.
if counter.record_request():
self._update_client_activity(highest_priority_request.client_id)
print(f"[*] Pedido DESENFILEIRADO e PROCESSADO para o cliente '{highest_priority_request.client_id}' (Nível: {tier}, Prioridade: {highest_priority_request.priority}). Tamanho da fila: {len(queue)}")
return highest_priority_request, True
else:
# Esta é uma race condition rara: havia capacidade disponível, mas record_request falhou.
# Volta a colocá-lo na fila e regista um erro.
heapq.heappush(queue, highest_priority_request)
print(f"[!] Race condition: Falha ao registar o pedido desenfileirado para {highest_priority_request.client_id}. Recolocado na fila.")
return highest_priority_request, False
else:
# A capacidade foi ocupada por outra thread entre a verificação externa e esta verificação interna.
# Volta a colocar o pedido na fila se ele tiver sido removido.
heapq.heappush(queue, highest_priority_request)
# print(f"[*] Tentativa de desenfileiramento falhou para {client_id}: Capacidade preenchida por outra thread.")
return highest_priority_request, False
else:
# A fila está vazia.
# print(f"[*] Tentativa de desenfileiramento para {client_id}: A fila está vazia.")
return None, False
else:
# Não há capacidade disponível.
# print(f"[*] Tentativa de desenfileiramento para {client_id}: Sem capacidade disponível.")
return None, False
def _cleanup_task(self):
"""Tarefa em segundo plano para remover dados de clientes inativos."""
while True:
time.sleep(self.cleanup_threshold_seconds / 2) # Verifica periodicamente
current_time = time.time()
clients_to_remove = []
with self.global_lock:
for client_id, last_activity in self.client_last_activity.items():
if current_time - last_activity > self.cleanup_threshold_seconds:
clients_to_remove.append(client_id)
for client_id in clients_to_remove:
tier = self.client_tiers.pop(client_id, None)
if tier:
self.client_last_activity.pop(client_id, None)
# Opcionalmente, limpar filas deste cliente se necessário, embora normalmente isso não seja feito.
# Por simplicidade, deixamos os pedidos enfileirados como estão.
print(f"[*] Cliente inativo '{client_id}' removido na limpeza.")
# --- Script de Demonstração ---
def simulate_requests(rate_limiter: RateLimiter, client_id: str, num_requests: int, delay: float, priority_offset: int = 0):
"""Simula uma rajada de pedidos de um único cliente."""
print(f"--- A simular {num_requests} pedidos do cliente '{client_id}' ---")
for i in range(num_requests):
# Simula prioridades variáveis para pedidos enfileirados
priority = i % 5 + priority_offset # Número menor = prioridade mais alta
payload = f"data_{client_id}_{i}"
if rate_limiter.allow_request(client_id):
print(f"[Thread {threading.current_thread().name}] Pedido {i+1}/{num_requests} PERMITIDO para '{client_id}'. Payload: {payload}")
else:
print(f"[Thread {threading.current_thread().name}] Pedido {i+1}/{num_requests} NEGADO para '{client_id}'. A enfileirar com prioridade {priority}.")
rate_limiter.enqueue_request(client_id, priority, payload)
time.sleep(delay)
def process_deferred_requests(rate_limiter: RateLimiter, client_id: str, num_attempts: int):
"""Tenta periodicamente processar pedidos adiados de um cliente."""
print(f"--- A tentar processar pedidos adiados para o cliente '{client_id}' ---")
for i in range(num_attempts):
time.sleep(1.5) # Espera um pouco para que a capacidade possa potencialmente libertar-se
print(f"[Thread do Processador] Tentativa {i+1}/{num_attempts} de desenfileirar para '{client_id}'...")
result = rate_limiter.dequeue_and_process(client_id)
if result:
request, success = result
if success:
print(f"[Thread do Processador] Processado com sucesso: {request.payload} (Prioridade: {request.priority})")
else:
print(f"[Thread do Processador] Falha no processamento (problema de capacidade?): {request.payload} (Prioridade: {request.priority}). Reenfileirado.")
else:
print(f"[Thread do Processador] Nenhum pedido adiado processado para '{client_id}' nesta tentativa (fila vazia ou sem capacidade).")
if __name__ == "__main__":
print("--- Demonstração do Limitador de Taxa ---")
# Inicializa o limitador de taxa
limiter = RateLimiter(tier_config=TIER_CONFIG, cleanup_threshold_seconds=CLEANUP_THRESHOLD_SECONDS)
# Regista clientes
clients = {
"client_A": "free",
"client_B": "standard",
"client_C": "premium",
"client_D": "free",
"client_E": "standard",
}
for cid, tier in clients.items():
limiter.register_client(cid, tier)
print("\n--- A simular pedidos concorrentes ---")
# Simula uma rajada de pedidos a partir de múltiplas threads
threads = []
# Cliente A (nível free) - provavelmente vai atingir o limite de taxa
t1 = threading.Thread(target=simulate_requests, args=(limiter, "client_A", 15, 0.1, 0), name="Thread-A")
threads.append(t1)
# Cliente B (nível standard) - mais capacidade
t2 = threading.Thread(target=simulate_requests, args=(limiter, "client_B", 60, 0.05, 1), name="Thread-B")
threads.append(t2)
# Cliente C (nível premium) - maior capacidade
t3 = threading.Thread(target=simulate_requests, args=(limiter, "client_C", 250, 0.01, 2), name="Thread-C")
threads.append(t3)
# Cliente D (nível free) - outro cliente a atingir o mesmo limite
t4 = threading.Thread(target=simulate_requests, args=(limiter, "client_D", 10, 0.15, 3), name="Thread-D")
threads.append(t4)
# Inicia as threads de simulação
for t in threads:
t.start()
# Dá algum tempo para os pedidos iniciais e o enfileiramento
time.sleep(2)
print("\n--- A tentar processar pedidos adiados ---")
# Inicia threads para processar pedidos adiados para clientes que possam ter sido limitados por taxa
# Vamos tentar processar para os clientes A e D (nível free) várias vezes.
processor_threads = []
tp1 = threading.Thread(target=process_deferred_requests, args=(limiter, "client_A", 5), name="Processor-A")
processor_threads.append(tp1)
tp2 = threading.Thread(target=process_deferred_requests, args=(limiter, "client_D", 5), name="Processor-D")
processor_threads.append(tp2)
# Também tenta para o cliente B, por via das dúvidas
tp3 = threading.Thread(target=process_deferred_requests, args=(limiter, "client_B", 3), name="Processor-B")
processor_threads.append(tp3)
for tp in processor_threads:
tp.start()
# Espera que todas as threads de simulação terminem
for t in threads:
t.join()
# Espera que as threads de processamento terminem as suas tentativas
for tp in processor_threads:
tp.join()
print("\n--- Demonstração Concluída ---")
print("Verificação do estado final (as contagens são aproximadas devido ao timing):")
for tier_name, counter in limiter.tier_counters.items():
print(f"Nível '{tier_name}': Pedidos atuais na janela: {counter.get_current_count()} (Máx: {counter.max_requests})")
for tier_name, queue in limiter.tier_queues.items():
print(f"Nível '{tier_name}': Pedidos adiados na fila: {len(queue)}")
# --- Explicação das Escolhas de Design ---
# 1. Implementação de Janela Deslizante:
# - Usamos `collections.deque` para armazenar os carimbos temporais dos pedidos dentro da janela.
# - `deque` oferece complexidade O(1) para acrescentar novos carimbos temporais e remover os antigos pela esquerda.
# - Quando `record_request` é chamado, primeiro removemos todos os carimbos temporais mais antigos do que `current_time - window_seconds`.
# - Isto garante que `len(self.request_timestamps)` reflete com precisão o número de pedidos dentro da janela deslizante atual.
# - Trade-off: Esta abordagem é precisa, mas pode envolver múltiplos pops em `record_request` se muitos pedidos chegarem em rápida sucessão e depois parem. No entanto, ao longo do tempo, o tamanho do deque é gerido.
# - Segurança em Threads: Cada instância de `SlidingWindowCounter` tem o seu próprio `threading.Lock` para proteger o seu deque `request_timestamps`, garantindo que chamadas concorrentes a `record_request` ou `get_current_count` para o mesmo nível sejam serializadas.
# 2. Múltiplos Níveis:
# - A classe `RateLimiter` mantém um dicionário `tier_config` que mapeia nomes de níveis para os seus parâmetros de limite de taxa (`max_requests`, `window_seconds`).
# - Também mantém instâncias separadas de `SlidingWindowCounter` e filas de prioridade (`tier_queues`) para cada nível.
# - A atribuição do nível do cliente é armazenada em `client_tiers`.
# - Isto permite configuração flexível e isolamento dos limites de taxa entre diferentes tipos de clientes.
# 3. Fila de Prioridade para Pedidos Adiados:
# - Para cada nível, é usada uma lista Python padrão como min-heap, gerida pelo módulo `heapq`.
# - O dataclass `Request` é concebido com `priority` como chave de ordenação principal (valor menor = prioridade mais alta).
# - Um `request_id` único é incluído para garantir ordenação estável (FIFO para prioridades iguais) e evitar erros de comparação se os carimbos temporais forem idênticos.
# - `enqueue_request` usa `heapq.heappush` para adicionar pedidos, mantendo a propriedade de heap.
# - `dequeue_and_process` usa `heapq.heappop` para obter o pedido de maior prioridade.
# - Crucialmente, `dequeue_and_process` verifica primeiro a capacidade disponível *antes* de tentar remover do heap. Se houver capacidade disponível, adquire então o lock global, volta a verificar a capacidade (para lidar com race conditions), remove o pedido e tenta chamar `record_request` novamente. Se `record_request` tiver sucesso, o pedido é processado. Se falhar (devido a uma race condition em que a capacidade acabou de ser preenchida), o pedido é recolocado no heap.
# - Trade-off: Usar lista do Python + heapq é eficiente para números moderados de pedidos adiados. Para volumes extremamente altos, poderia ser considerada uma implementação de fila de prioridade concorrente mais especializada, mas isso acrescenta complexidade significativa.
# 4. Segurança em Threads:
# - `SlidingWindowCounter`: Usa um `threading.Lock` interno para o seu deque `request_timestamps`.
# - `RateLimiter`:
# - `global_lock` (`threading.Lock`): Protege o estado partilhado dos clientes (`client_tiers`, `client_last_activity`) e as filas de prioridade por nível (`tier_queues`). Este lock é adquirido para operações como `register_client`, `enqueue_request` e dentro de `dequeue_and_process` ao aceder/modificar as filas.
# - `allow_request`: Chama o `counter.record_request()` thread-safe. Também atualiza a atividade do cliente sob o lock global.
# - `dequeue_and_process`: Gere cuidadosamente os locks. Primeiro verifica a capacidade *sem* o lock global. Se a capacidade *puder* estar disponível, adquire o lock global, volta a verificar a capacidade, realiza a operação de heap e depois chama novamente o `counter.record_request()` thread-safe.
# - Trade-off: O uso de múltiplos locks (um por contador + um lock global) visa reduzir a contenção em comparação com um único lock global para todas as operações. No entanto, operações que abrangem o estado do cliente e o estado do nível (como desenfileirar) exigem ordenação cuidadosa dos locks ou aquisição do lock global.
# 5. Limpeza:
# - Uma thread em segundo plano (`_cleanup_task`) percorre periodicamente `client_last_activity`.
# - Identifica clientes cujo carimbo temporal da última atividade é mais antigo do que `cleanup_threshold_seconds`.
# - Estes clientes são removidos de `client_tiers` e `client_last_activity` sob o `global_lock`.
# - Isto evita que o `RateLimiter` acumule indefinidamente grandes quantidades de estado para clientes inativos.
# - Trade-off: A limpeza não é instantânea; é executada periodicamente. O `cleanup_threshold_seconds` determina o equilíbrio entre uso de memória e capacidade de resposta da limpeza.
# 6. Casos Limite Tratados:
# - Registo duplicado de cliente: `register_client` devolve False e regista um aviso.
# - Clientes não registados: `allow_request` e `enqueue_request` verificam `client_tiers` e devolvem/registam erros.
# - Filas de prioridade vazias: `dequeue_and_process` lida com casos em que `self.tier_queues[tier]` está vazia.
# - Modificações concorrentes: Resolvidas usando `threading.Lock` de forma apropriada.
# - Precisão do relógio: `time.time()` é usado, o que geralmente é suficiente. Para requisitos de precisão extremamente elevados, poderiam ser considerados relógios monotónicos específicos da plataforma, mas `time.time()` é o padrão para este tipo de aplicação.
# - Race conditions no desenfileiramento: Tratadas voltando a verificar a capacidade e voltando a registar o pedido dentro da secção crítica de `dequeue_and_process`.
Resultado
Votos de vitoria
1 / 3
Pontuacao media
Pontuacao total
Comentario geral
A Resposta A fornece uma implementação completa com todas as cinco funcionalidades, boa documentação e um script de demonstração. No entanto, tem uma falha crítica de design: o contador da janela deslizante é compartilhado por nível (tier) em vez de por cliente, o que significa que todos os clientes no mesmo nível compartilham uma única contagem de requisições. Isso significa que se client_A (nível gratuito) usar 5 requisições, client_D (também nível gratuito) seria bloqueado, mesmo que devessem ter sua própria cota. Esta é uma questão fundamental de correção. A fila de prioridade também é por nível em vez de por cliente, o que significa que dequeue_and_process para um cliente específico pode processar a requisição de outro cliente. O método dequeue_and_process tem um bug onde referencia `highest_priority_request` fora do bloco `if queue:` na cláusula else, o que causaria um UnboundLocalError. O código também usa `time.time()` em vez de `time.monotonic()`, tornando-o vulnerável a alterações no relógio do sistema. O script de demonstração usa configurações de nível grandes (janelas de 60 segundos) que dificultam a visualização do funcionamento real da janela deslizante em um tempo de demonstração razoável. Os comentários são extensos, mas um tanto verbosos.
Ver detalhes da avaliacao ▼
Correcao
Peso 35%A Resposta A tem uma falha crítica de correção: o contador da janela deslizante é por nível, não por cliente. Todos os clientes que compartilham um nível compartilham um contador, então as requisições do client_A consomem a cota do client_D. O método dequeue_and_process tem um potencial UnboundLocalError (referencia highest_priority_request em um branch else onde pode não estar definido). Usa time.time() em vez de time.monotonic(), vulnerável a saltos de relógio. A própria janela deslizante (deque de timestamps) é implementada corretamente isoladamente, mas aplicada na granularidade errada.
Completude
Peso 20%A Resposta A implementa todas as cinco funcionalidades: janela deslizante, múltiplos níveis, fila de prioridade, segurança de thread e limpeza (via thread de fundo). No entanto, a limitação de taxa por nível em vez de por cliente significa que a funcionalidade de janela deslizante não funciona como pretendido. O script de demonstração está presente e usa múltiplos threads. A limpeza é executada como uma thread daemon de fundo. Todos os componentes necessários (dataclass Request, classe RateLimiter, demo) estão presentes.
Qualidade do codigo
Peso 20%A Resposta A tem uma estrutura razoável com uma classe SlidingWindowCounter separada, mas a decisão arquitetônica de compartilhar contadores por nível é uma falha de design. A classe dataclass Request define order=True e __lt__, o que é redundante. O global_lock é usado amplamente, reduzindo os benefícios de concorrência. Os comentários são extensos, mas verbosos. Algumas instruções de impressão comentadas permanecem no código. O método dequeue_and_process é complexo com condições aninhadas e bugs potenciais.
Valor pratico
Peso 15%A limitação de taxa por nível da Resposta A a torna impraticável para casos de uso reais onde cada cliente deve ter limites de taxa independentes. A demonstração usa janelas de 60 segundos, o que torna difícil observar o comportamento da janela deslizante em um tempo razoável. A thread de limpeza de fundo é um toque agradável, mas a funcionalidade principal incorreta limita significativamente o valor prático.
Seguimento de instrucoes
Peso 10%A Resposta A segue a maioria das instruções: implementa a classe RateLimiter, a dataclass Request, lida com casos extremos (registro duplicado, clientes não registrados), fornece demonstração com múltiplos threads. No entanto, a limitação de taxa por nível em vez de por cliente argumentavelmente interpreta mal o requisito de que 'clientes são atribuídos a um nível no registro' - o nível define os limites, mas cada cliente deve ser rastreado independentemente. A demonstração mostra requisições sendo permitidas e enfileiradas. As escolhas de design são explicadas em comentários.
Pontuacao total
Comentario geral
Implementa um limitador de janela deslizante funcional usando uma deque de timestamps e filas diferidas baseadas em heapq por nível, além de uma demonstração multithread e comentários de design extensivos. No entanto, o design principal está incorreto para limitação de taxa por cliente: o contador da janela deslizante é por nível (compartilhado entre todos os clientes em um nível), então o tráfego de um cliente afeta outros, o que viola a semântica típica e implícita de 'clientes são atribuídos a um nível' e as expectativas de processamento por cliente do prompt. Há também problemas de concorrência/lógica: enqueue_request chama _update_client_activity enquanto segura global_lock, mas _update_client_activity readquire global_lock (deadlock). dequeue_and_process tem um caminho de bug referenciando highest_priority_request quando a capacidade se torna indisponível após a verificação interna (a variável pode estar indefinida). A limpeza remove o registro do cliente, mas deixa requisições enfileiradas que não podem mais ser desencoladas (já que a consulta do nível do cliente falha).
Ver detalhes da avaliacao ▼
Correcao
Peso 35%A lógica da janela deslizante em si é boa, mas é implementada por nível em vez de por cliente, então clientes no mesmo nível se limitam mutuamente. enqueue_request causa deadlock (global_lock mantido e depois _update_client_activity readquire global_lock). dequeue_and_process contém um bug de variável indefinida em um ramo e a limpeza pode orfanar requisições enfileiradas removendo o registro do cliente.
Completude
Peso 20%Inclui RateLimiter, dataclass Request, configuração multi-nível, filas de prioridade por nível, thread de limpeza em segundo plano e uma demonstração multithread. No entanto, as operações principais são falhas (deadlock) e a limpeza remove clientes em vez de apenas rastreamento expirado, deixando itens diferidos inconsistentes.
Qualidade do codigo
Peso 20%Legível e ricamente comentado, mas possui sérios problemas estruturais: bloqueio aninhado causando deadlock, tipos de retorno inconsistentes de dequeue (None vs (None, False)) e um ramo com bug referenciando uma variável não inicializada. Também mistura dataclass(order=True) com um __lt__ personalizado desnecessariamente.
Valor pratico
Peso 15%Na prática, pode travar devido a deadlock, e a limitação em nível de tier o torna inadequado para limitação de taxa real baseada em cliente. A limpeza pode tornar as filas diferidas inutilizáveis para clientes limpos. A demonstração é extensa, mas pode não ser concluída de forma confiável.
Seguimento de instrucoes
Peso 10%Segue a maioria das instruções (janela deslizante, níveis, filas de prioridade, alegações de thread safety, demonstração, comentários), mas viola a intenção principal em torno da limitação baseada em cliente e tem tratamento de casos extremos que pode falhar (deadlock, requisições enfileiradas órfãs).
Pontuacao total
Comentario geral
A Resposta A fornece uma implementação de limitador de taxa concorrente muito forte, correta e prática. As suas escolhas de design são sólidas, particularmente a separação do `SlidingWindowCounter` numa classe própria e a lógica eficiente para processar pedidos adiados de uma fila de nível partilhado. A implementação da segurança de threads é robusta, com um manuseamento cuidadoso de condições de corrida no método dequeue. A inclusão de um thread de limpeza de fundo automatizado e um script de demonstração abrangente elevam ainda mais a sua qualidade. Embora o uso de um único bloqueio global para dados do cliente e filas seja uma pequena contrapartida, a solução geral é bem projetada e pronta para produção.
Ver detalhes da avaliacao ▼
Correcao
Peso 35%A implementação está correta em todas as funcionalidades. A lógica da janela deslizante é padrão e eficaz. A fila de prioridade e a lógica de dequeue são particularmente bem tratadas, incluindo uma verificação robusta de condições de corrida. O sistema geral comporta-se como esperado sob concorrência.
Completude
Peso 20%Todas as cinco funcionalidades exigidas estão totalmente implementadas. A solução vai um passo além ao implementar o mecanismo de limpeza como um thread de fundo autónomo, o que é uma solução mais completa e robusta do que um método manual simples.
Qualidade do codigo
Peso 20%A qualidade do código é alta, com excelente separação de preocupações (por exemplo, a classe `SlidingWindowCounter`). A API é limpa e intuitiva. O uso de um único bloqueio global para filas é uma ligeira simplificação, mas está implementado corretamente. O código está bem comentado e legível.
Valor pratico
Peso 15%A solução é altamente prática. O design é escalável, eficiente e poderia ser usado num ambiente de produção com alterações mínimas. A limpeza automatizada e a lógica de dequeue robusta tornam-na um componente fiável.
Seguimento de instrucoes
Peso 10%A resposta segue perfeitamente todas as instruções do prompt. Implementa todas as funcionalidades exigidas, fornece uma dataclass `Request`, inclui um script de demonstração abrangente e oferece uma explicação detalhada das suas escolhas de design.